---
title: "Analyse a brand's website into a brand profile"
method: POST
path: "/image-briefs/brand-voice/analyze"
tags: ["Briefs"]
---

# Analyse a brand's website into a brand profile

`POST /image-briefs/brand-voice/analyze`

## Request

- Base URL: `https://api.reacherapp.com/public/v1`
- URL: `https://api.reacherapp.com/public/v1/image-briefs/brand-voice/analyze`
- Auth: API key in header `x-api-key` and API key in header `x-shop-id`

Scrape the brand's website and extract a brand profile — name, voice descriptors, target audience, categories — and store it for the shop. This is what `PUT /image-briefs/brand-voice` depends on: a shop that has never had brand analysis run 404s there until this endpoint has created the profile. After it runs, brand voice is populated (and can be overridden with the PUT).

It scrapes the site and runs an LLM, so it is a write: it needs `read_write` scope, counts against the write rate limit, and the shop's existing profile is returned as-is unless `refresh: true` (a shop has one profile). A required `Idempotency-Key` makes a retry replay rather than re-scrape; `X-Dry-Run: true` validates without scraping. Single shop only.

## Headers

- `Idempotency-Key` string, nullable
- `X-Dry-Run` string, nullable

## Request body

- ImageBriefBrandAnalyzeRequest
  - `website_url` string, required: The brand's public website URL to scrape and analyse (e.g. https://www.thebean.com).
  - `refresh` boolean: Re-run the analysis even if a profile already exists for this URL.

## Response `201`

Successful Response

- BrandProfileWriteResponse
  - `data` BrandProfile, required: The analysed brand profile stored for the shop.
    - `website_url` string, nullable
    - `brand_name` string, nullable
    - `voice_descriptors` string[]
    - `target_audience` string, nullable
    - `extracted_categories` string[]
    - `extraction_model` string, nullable
    - `extracted_at` string, nullable
    - `source` string, nullable: `analyzed` (freshly extracted) or `cache` (already had one).
  - `dry_run` boolean

## Other responses

- `400`: Validation error, multi-shop key, or missing/invalid Idempotency-Key.
- `403`: API key lacks `read_write` scope (`WRITE_NOT_PERMITTED`).
- `404`: Brief or product not found in this shop.
- `409`: Idempotency conflict.
- `422`: Validation Error
- `429`: Write-tier rate limit exceeded.

## Changes

> 76 revisions in range; 1 not diffed.

- **2026-09-19** `291230805105`: 1 breaking
  - the `data/extracted_at/anyOf[subschema #1]/` response's property format changed from `date-time` to no format for status `201`
- **2026-09-09** `d0d1c994bfd5`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/reacherapp/apis/reacher-data-api/changes/image-briefs/brand-voice/analyze/post.md)

---

[API](https://skmtc.dev/reacherapp/apis/reacher-data-api.md) - [All operations](https://skmtc.dev/reacherapp/apis/reacher-data-api/llms.txt) - [OpenAPI document](https://skmtc.dev/reacherapp/apis/reacher-data-api/revisions/63ce74b30b5a?raw)
