The examples below use
https://www.merchkit.com as the base URL.The first five minutes
Three calls take you from a fresh API key to reading real catalog data.1
Authenticate
Every request carries your workspace API key as a bearer token:One key maps to exactly one workspace, and keys carry scopes. See
Authentication for the full model.
2
Learn your keys
A product’s fields are attributes, defined per workspace rather than hard-coded. Your
first real call is schema discovery. It returns every key you can read and write, its data
type, whether it is writable, and (for selects) the exact acceptable values:Cache this. It is the canonical answer to “what fields exist”, and it rarely changes.
3
Read a product
Every entity uses the same one-map shape, whether it is a list row, a detail read, or a
mutation response: system fields at the top level, every customer-defined key inside
attributes. References come back as labeled stubs, so you never do a second lookup just to
render a name:attributes is sparse: only keys with values appear. Read with attributes[key] ?? null,
render names with label ?? id. The full anatomy lives in
Conventions.4
Find anything by SKU
filter[key]=value means equals. The full
filter grammar covers the rest.The identity rule
The UUID is the address. Your SKU is a filter. Item paths take UUIDs only
(
GET /v1/products/{id}), and reference values are UUIDs. To resolve your own identifier, filter
with ?filter[sku]=ARIA-DT-72, or resolve 100 at once with ?filter[sku][in]=A,B,C. When you’d
rather not resolve IDs at all, upsert and batch write by your own key: POST /v1/products/upsert
and POST /v1/products/batch match on any text attribute you name (merge_key, usually sku) and
create or update accordingly.The mental model
These concepts cover how every resource behaves.1
Five resources, one shape
products, images, vendors, categories, and sources are the same kind of object, an
entity with dynamic attributes, and share identical list/read/write mechanics. type
discriminates (product | image | vendor | category | source).2
Attributes are dynamic — and sparse
Attribute definitions describe what an entity can hold;
attributes holds what it does
hold. To enrich a product you PATCH attribute values. You never recreate the product to
change a field. Videos are not a resource: a generated video is a plain URL value inside
attributes, like any other scalar.3
Sources are async
A source is a web page or document Merchkit scrapes for enrichment. Processing is
asynchronous:
POST /v1/sources with a URL returns 202 and a job_id. Poll
GET /v1/jobs/{job_id} until completed, then find the created source via
GET /v1/sources?filter[url]=.... Attaching
a source to a product is just a reference write: set data_sources in the product’s
attributes.4
Completeness is per-channel
Attributes can be channel-scoped, so completeness is evaluated per channel. The same
product can be complete for one channel and incomplete for another. Always pass
?channel=.Channels are read-only and global in the API. You read the enabled channel catalog via
GET /v1/channels, but you cannot create channels through the API.Pushing a catalog from an ERP
A 5,000-SKU delta push is 50 calls, with no ID-mapping table and no create-vs-update fork on your side.pricee), which shows what a failed item looks like:
- Check
summary.has_errorsfirst. Clean batches need no per-item scan, andsummary.failedis your log line and alerting metric. - Join results to your source rows by
merge_value, not by array position. It survives sorting, re-sliced batches, and error reports read days later. (indexis still there and always matches input order.) - Route retries on
error.retriable, never on message text.retriable: falsemeans the same item will fail the same way, so dead-letter it for a human.retriable: true(rate limits, transient server errors) belongs in your retry queue. Don’t sumsummarycounts across re-runs of the same batch: an idempotent retry reportsupdatedfor rows the first attemptcreated.
sku (trimmed, case-insensitive) and created or updated accordingly, with a
per-item status. Re-running the same batch is idempotent. Check every item: an HTTP 200 does not
mean every item succeeded, and failed items carry an error instead of an id. Up to 100 items
per call; omit merge_key for pure creates.
Single-record flows use POST /v1/products/upsert with the same semantics. The status code tells you
what happened, and both outcomes return the full resource, so no follow-up GET is needed:
201 Created):
200 OK), with
the same id, merged attributes, and a bumped updated_at:
409 Conflict
listing the matching ids. Deduplicate, or update one of them directly by id.
Base URL and spec
string
https://www.merchkit.com/api/v1. All REST endpoints live under the /api/v1 prefix.string
https://www.merchkit.com/api/v1/openapi is the machine-readable contract, with worked examples on
every operation. It backs the API Reference in these docs.string
https://www.merchkit.com/llms.txt is a curated, instruction-bearing guide written for agents. It
carries the identity rule, the filter grammar, and the common recipes. If you are pointing an
agent at Merchkit, give it this URL.Where to go next
Authentication
API keys, OAuth for agents, and the per-resource scope taxonomy.
Conventions
Resource anatomy, the filter grammar, pagination, events polling, and async jobs.
Errors
Every error code, every field-level issue, and how to recover from each.
MCP server
Connect Claude, ChatGPT, or VS Code in one click, with the same shapes as REST.