> ## Documentation Index
> Fetch the complete documentation index at: https://docs.semicola.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Catalogs

> Product, job, store and offer feeds: supported formats, versions, transforms and activation.

A **catalog** is a feed of items (products, jobs, stores, offers) that belongs to an advertiser.
Semi reads it on a schedule, keeps a version each time the items change, and can fan it out into
campaigns, creative and seller feeds.

## Supported formats

| Format                     | What we read                                                                                    |
| -------------------------- | ----------------------------------------------------------------------------------------------- |
| JSON                       | An array of items, or an object with `items`, `products`, `entries`, `data`, `jobs` or `stores` |
| CSV / TSV                  | A header row, then one item per line                                                            |
| RSS / Atom XML             | Each `<item>` or `<entry>`                                                                      |
| Google Merchant Center XML | `<item>` elements with `g:` fields (`g:id`, `g:title`, `g:price`, …)                            |

Every item needs an id. The first of `id`, `sku`, `offer_id`, `item_id`, `product_id`, `job_id`,
`store_id` or `guid` is used. Field names are normalized to `snake_case` and the `g:` prefix is
dropped. A feed can hold up to 5,000 items and 20 MB.

## Connecting a feed

Ask Semi to connect a feed URL, or call `sync_catalogs`:

```json theme={null}
{
  "advertiserId": 201,
  "catalogs": [
    {
      "name": "Product feed",
      "type": "product",
      "url": "https://example.com/feed.json",
      "updateFrequency": "daily"
    }
  ]
}
```

The feed is fetched right away. Each fetch is a **refresh run** (status, HTTP status, item count).
When the items changed, a new **version** is stored with its change summary (added, updated,
removed, unchanged). Inline items (`items: [...]`) work the same way without a URL.

A feed is **stale** once twice its update frequency has passed without a successful refresh.

## Transform and activation

A **transform** says how to fan the catalog out:

* `groupBy`: item fields that split items into campaign groups (for example `category`)
* `creativePrompt`: the creative brief per group; `{field}` placeholders take the group's values
* `budgetPerGroup`: the budget each campaign group starts with, in the advertiser's currency

`preview_catalog_activation_plan` shows the campaign groups, creative assets and seller targets.
`execute_catalog_activation_plan` creates one **draft** campaign per group (nothing is booked or
spent), queues creative generation, and shares the catalog over AdCP `sync_catalogs` with the
catalog-driven sellers you buy from. Sellers that take assembled creative wait for the creative;
sellers without a catalog or creative handoff are skipped. Sellers with **Audiences** turned off in
[Connections](/buy/connections#distribution) are skipped too, with the reason "Audiences is turned
off for this seller in Connections, so no feed data is shared with it." The preview shows it, and
execution checks the switch again.

## Find and read catalogs

`search` with `kind: "catalog"` lists an advertiser's live catalogs. Pass `filter.advertiserId`, and
optionally `filter.type` and a `query` matched against the name and platform id. `get` with
`kind: "catalog"`, the `catalogId` and the `advertiserId` reads one catalog. Add
`include: ["items"]` to get a page of its latest items too: up to 50, or fewer with `limit`. Narrow
the items with `filter`: `ids`, `gtins` and `tags` (lists; an item matches any value), `category`
(matched against the item's category or product type) and `query` (contained in the id, title, name
or description). `itemsPage` returns `nextCursor`, `hasMore` and how many items `matched`; pass
`cursor: itemsPage.nextCursor` for the next page. Items here aren't content-reviewed, so
`filter.status` is refused with `CAPABILITY_NOT_SUPPORTED`. Any other include is listed in
`unavailableIncludes`.

## REST

| Method | Path                                                                          |
| ------ | ----------------------------------------------------------------------------- |
| GET    | `/api/v2/buyer/advertisers/{id}/catalogs`                                     |
| POST   | `/api/v2/buyer/advertisers/{id}/catalogs/sync`                                |
| PUT    | `/api/v2/buyer/advertisers/{id}/catalogs/{catalogId}/transform`               |
| POST   | `/api/v2/buyer/advertisers/{id}/catalogs/{catalogId}/activation-plan/preview` |
| POST   | `/api/v2/buyer/advertisers/{id}/catalogs/{catalogId}/refresh`                 |
| POST   | `/api/v2/buyer/advertisers/{id}/catalogs/{catalogId}/activation-plan/execute` |
