API reference

Every v1 endpoint. Base URL https://app.peak-pim.com, bearer-key auth, JSON responses. The machine-readable contract is the OpenAPI 3.1 spec — this page is the same content for humans.

Products, variants, collections, and media files share one uniform endpoint family. For each entity type {resource}products | variants | collections | media:

EndpointReturns
GET /api/v1/{resource}One cursor page of list items
GET /api/v1/{resource}/{id}One entity — canonical values, relationships, per-store publish state
GET /api/v1/{resource}/{id}/storesEvery store version of the entity
GET /api/v1/{resource}/{id}/stores/{store_id}One store's version of the entity

All endpoints on this page require the pim:read scope.

Service

GET/api/v1/ping

Verify a key. Answers 200 once the key has passed authentication, the route allowlist, its scope, and the rate limiter — use it to check a new key end to end before wiring real calls.

{"status":"ok","account_id_present":true}

List endpoints

GET /api/v1/products · /variants · /collections · /media

One cursor page of the account's entities of that type, ordered oldest change first so a poller can walk the whole catalog and stop when next_cursor is null.

Query parameters

ParameterTypeDescription
limitintegerRows per page. Default 50, maximum 250; values above the maximum are clamped, non-numeric values fall back to the default.
cursorstringThe next_cursor of the previous page. Opaque — do not build or parse one. A cursor from a different list is rejected with validation_failed.
updated_sinceRFC3339 date-timeOnly entities whose own (canonical) record changed at or after this time. Store-version edits do not widen it. Non-RFC3339 values are rejected with validation_failed.
statusenumPublish-state filter: never_published, pending, published, failed.
searchstringFree-text match. Products: name, store title, vendor, tags, or a variant SKU. Variants: name, SKU, or parent product's name/title/vendor/tags. Collections: name or store title. Media: filename, tags, or alt text.

Response — 200

{
  "data": [
    {
      "id": "uuid",
      "type": "product | variant | collection | media",
      "title": "…",
      "draft_revision": 14,
      "pending_publish": true,
      "last_publish_status": "never_published | pending | published | failed",
      "store_ids": ["uuid", "…"]
    }
  ],
  "next_cursor": "opaque-string-or-null"
}

Media files are named by their filename, so for media search matches the filename and tags, and title carries the filename.

Entity endpoints

GET /api/v1/{resource}/{id}

One entity's canonical values — the values every store inherits — plus its relationships and where it stands with each store it lives on.

Response — 200

{
  "id": "uuid",
  "type": "product",
  "title": "…",
  "attributes": [ { "key": "…", "value": "…", "type": "…" } ],
  "draft_revision": 14,
  "pending_publish": true,
  "last_publish_status": "published",

  "product_id": "uuid",          // variants only — the parent product
  "variant_ids": ["uuid"],       // products only — the product's variants
  "media_ids": ["uuid"],         // gallery order, across every store; absent on media

  "stores": [
    {
      "store_id": "uuid",
      "store_domain": "acme.myshopify.com",
      "pending_publish": true,
      "last_publish_status": "published"
    }
  ]
}

stores carries publish state only; a store's values live on the store subresources below. draft_revision is the optimistic-concurrency handle a later write sends back to prove it read the current state.

GET /api/v1/{resource}/{id}/stores

Every store version of the entity. Bounded by the account's stores, so the whole list is returned and next_cursor is always null.

{
  "data": [
    {
      "store_id": "uuid",
      "store_domain": "acme.myshopify.com",
      "attributes": [ { "key": "…", "value": "…", "type": "…" } ],
      "pending_publish": false,
      "last_publish_status": "published"
    }
  ],
  "next_cursor": null
}
GET /api/v1/{resource}/{id}/stores/{store_id}

One store's version of the entity: the values that store publishes, which may differ from every other store's. store_id comes from GET /api/v1/stores. An entity with no version on the requested store answers 404 not_found.

Stores

GET/api/v1/stores

The Shopify stores linked to the account. The whole list is returned; next_cursor is always null.

{
  "data": [
    { "id": "uuid", "domain": "acme.myshopify.com", "name": "Acme US" }
  ],
  "next_cursor": null
}

Response headers

HeaderMeaning
ETagOn every 200. Send back as If-None-Match to get a 304 instead of a body.
X-RateLimit-LimitRequests allowed for this key in the current minute.
X-RateLimit-RemainingRequests left in the current minute.
X-RateLimit-ResetUnix second the current minute window resets.
X-Request-IdCorrelation id for this request — quote it in support requests.
Retry-AfterOn 429 only: seconds to wait before retrying.

Status codes

200 success · 304 not modified (conditional read) · 400 validation failed · 401 unauthorized · 403 forbidden / wrong scope · 404 not found · 429 rate limited · 5xx internal error. Full semantics and the error envelope: Errors & limits.