---
title: "Start Brand Extraction"
method: POST
path: "/brand-kits/extractions"
tags: ["brand-kits"]
---

# Start Brand Extraction

`POST /brand-kits/extractions`

## Request

- Base URL: `https://api.moda.app/v1`
- URL: `https://api.moda.app/v1/brand-kits/extractions`
- Auth: HTTP bearer

Read a website's brand in the background WITHOUT creating a brand kit.

For callers that keep the kit themselves and use Moda only to extract it. Runs the
same extraction as ``POST /v1/brand-kits`` with ``url`` (15-32 s cold), so it is a
job: ``202`` with a ``job_id`` now, then poll ``GET /v1/brand-kits/extractions/{job_id}``
until ``status`` is ``completed`` (the brand rides along as ``extraction``) or
``failed``. Nothing is added to the team's brand kits, drive or library. The logo and
screenshot files are kept for 24 hours so their URLs can be served: attach a
``file_id`` to a kit in that window (``logo_file_ids`` on create, or
``POST /v1/brand-kits/{id}/images``) and the kit keeps the file. Otherwise the
``file_id`` stops resolving after that; URLs already signed keep working until they
lapse, so download what you keep. Needs ``brand_kits:write`` because it spends the
extraction. Honors ``idempotency_key``. Not metered.

## Headers

- `Moda-Version` '2026-04-12' | '2026-05-01'

## Request body

- StartBrandExtractionRequest: Request body for ``POST /v1/brand-kits/extractions`` — read a website's brand, keep no kit.
  - `url` string, required: Company website URL or domain, e.g. 'stripe.com'.
  - `idempotency_key` string, nullable: A retried start with the same key returns the first job instead of starting another. Same-key reuse with a different payload is a 409 ``idempotency_conflict``.

## Response `202`

Successful Response

- BrandExtractionJob: A website → brand extraction, as returned by start (``202``) and by the status poll.
  - `job_id` string, required: Extraction job id; poll ``GET /v1/brand-kits/extractions/{job_id}``.
  - `status` 'queued' | 'running' | 'completed' | 'failed', required: ``queued`` → ``running`` → ``completed`` or ``failed``. Only the last two are final.
  - `message` string, required: Human-readable progress or outcome.
  - `error` string, nullable: Failure reason when ``status`` is ``failed``.
  - `code` string, nullable: Machine-readable failure when ``status`` is ``failed``: ``scrape_failed`` (the site could not be read or yielded nothing), ``extraction_timeout`` or ``extraction_failed``.
  - `retry_after_seconds` integer, nullable: Suggested wait before the next poll; null once the job is final.
  - `extraction` object, nullable: The brand, once ``status`` is ``completed``: ``company_name``, ``company_url``, ``company_description``, ``tagline``, ``brand_values``, ``brand_tone_of_voice``, ``colors``, ``fonts``, ``logos`` (each image with a ``file_id`` and a signed ``url``), ``screenshot`` (``{file_id, url, captured_at, source_url}`` or null) and ``personality`` (``{tone, energy}`` or null) — the brand fields of a kit read, with no kit created. URLs are signed at each poll. The files are kept for 24 hours: a ``file_id`` attached to a brand kit in that window stays with the kit; any other stops resolving after it (URLs already signed keep working until they lapse).
  - `replayed` boolean, nullable: ``true`` when an idempotency key replayed the first start.

## Other responses

- `401`: Authentication required.
- `402`: Insufficient credits for a metered operation.
- `403`: Permission denied for this scope.
- `404`: Resource not found.
- `409`: Conflict (idempotency / resource state).
- `422`: Request validation failed.
- `429`: Rate limit exceeded.
- `500`: Internal error.

## Changes

- **2026-11-03** `9ac6a3abc0f8`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/moda/apis/moda-public-api/changes/brand-kits/extractions/post.md)

---

[API](https://skmtc.dev/moda/apis/moda-public-api.md) - [All operations](https://skmtc.dev/moda/apis/moda-public-api/llms.txt) - [OpenAPI document](https://skmtc.dev/moda/apis/moda-public-api/revisions/9ac6a3abc0f8?raw)
