---
title: "Push a catalog"
method: POST
path: "/v1/catalog"
tags: ["Store"]
---

# Push a catalog

`POST /v1/catalog`

## Request

- Base URL: `https://carbonguru.io`
- URL: `https://carbonguru.io/v1/catalog`
- Auth: HTTP bearer

Up to 500 rows, each carrying the store's one country. Needs an API key: the intake names the merchant by the key, so an OAuth token receives 403 key_required. 202 with the push id when rows were queued, or when nothing was queued and the push record exists; 200 with a null push_id and a reason when nothing was queued and nothing was recorded. Rows inside the allowance are analysed and the rest are deferred and reported on the push record — a push is never refused for size. An unchanged row takes the content-hash exit and costs nothing; force re-analyses every row.

## Headers

- `Idempotency-Key` string

## Request body

- object
  - `rows` CatalogRow[], required
    - `product_name` string, required
    - `price` object, required
      - `current_price` number, required: In major units of the row's currency: 1,000 JPY is 1000.
      - `currency` string, required: ISO 4217. It must be the currency the platform holds for the declared country, or the row is skipped.
    - `geographic_context` object, required
      - `country_code` string, required: The country the merchant declares this product is sold in. No default exists.
    - `identifiers` string[]: The row's identity. The first one is the row's reference everywhere else in this API.
    - `source_url` string: Must be on the merchant's registered domain, or the row is keyed by its first identifier instead.
    - `category` string
    - `description` string
    - `materials` string
    - `origin_country` string
    - `variation_of` string: The parent's primary identifier, when this row is one variation of a product.
  - `force` boolean: Re-analyse every row, unchanged content included. Each re-analysis is billable.

## Response `200`

Nothing was queued and no push record was written. reason names why and push_id is null.

- CatalogPush: What the intake did with the pushed rows. push_id is null only when nothing was queued and no push record was written.
  - `push_id` string, nullable, required: The push's id, which reads back at GET /v1/pushes/{push_id}.
  - `counts` object, required
    - `analyzed_now` integer, nullable: Rows queued for analysis. These are the billable rows.
    - `already_analyzed` integer, nullable: Rows whose content is unchanged, in flight, or cooling down. Not billed.
    - `deferred` integer, nullable: Rows past the allowance. They stay on the push record.
    - `skipped_total` integer, nullable: Rows refused at intake, before any truncation of the list below.
  - `skipped` object[], required: Up to 50 refused rows, each with its position and stated reason. The full list is on the push record.
  - `allowance_remaining` integer, nullable: Analyses left in the period. Null means unlimited.
  - `reason` string, nullable: Present on the 200: the intake's own word for why nothing was queued, such as not_entitled or allowance_reached.
  - `links` object, required
    - `push` string

## Other responses

- `202`: The push was accepted. Poll it at links.push.
- `401`: No valid credential. Every authentication failure returns this one document.
- `402`: The account's plan does not include this capability. Carries min_tier and upgrade_url.
- `403`: A missing scope, an exhausted analysis allowance, or an object that is not the caller's.
- `404`: No such resource.
- `409`: An Idempotency-Key was reused with a different body (idempotency_key_reuse), or a request under the same key is still running (idempotency_in_progress).
- `422`: The request's fields are wrong. errors[] names them.

---

[API](https://skmtc.dev/carbonguru/apis/carbonguru-api.md) · [All operations](https://skmtc.dev/carbonguru/apis/carbonguru-api/llms.txt) · [OpenAPI document](https://skmtc.dev/carbonguru/apis/carbonguru-api/revisions/26dcbe7a8500?raw)
