Getting started
Quickstart
Start with catalog reads, then create a product, edit a metafield and publish it. All you need is
curl (and optionally jq).
1 · Create a key and verify it
Create a key with the pim:read scope in
Settings → API, export it,
and ping:
export PEAK_PIM_KEY="pk_live_..."
curl https://api.peak-pim.com/api/v1/ping \
-H "Authorization: Bearer $PEAK_PIM_KEY"
{"status":"ok","account_id_present":true}
A 200 here proves the key end to end — authentication, route allowlist, scope, and rate limiter. Anything else: see Errors.
2 · List your stores
curl "https://api.peak-pim.com/api/v1/stores" \
-H "Authorization: Bearer $PEAK_PIM_KEY"
{
"data": [
{ "id": "1e8e…", "domain": "acme.myshopify.com", "name": "Acme US" },
{ "id": "9c1f…", "domain": "acme-eu.myshopify.com", "name": "Acme EU" }
],
"next_cursor": null
}
Keep a store id around — you'll use it in step 5.
3 · List products
curl "https://api.peak-pim.com/api/v1/products?limit=5" \
-H "Authorization: Bearer $PEAK_PIM_KEY"
{
"data": [
{
"id": "7b2a…",
"type": "product",
"title": "Trail Runner 2",
"draft_revision": 14,
"pending_publish": true,
"last_publish_status": "published",
"store_ids": ["1e8e…", "9c1f…"]
}
],
"next_cursor": "eyJv…"
}
To walk the whole catalog, pass next_cursor back as
cursor until it comes back null:
curl "https://api.peak-pim.com/api/v1/products?limit=250&cursor=eyJv..." \
-H "Authorization: Bearer $PEAK_PIM_KEY"
Useful filters (combinable): search=trail,
status=pending, updated_since=2026-07-01T00:00:00Z —
full semantics in Pagination & filtering.
4 · Read one product
curl "https://api.peak-pim.com/api/v1/products/7b2a..." \
-H "Authorization: Bearer $PEAK_PIM_KEY"
{
"id": "7b2a…",
"type": "product",
"title": "Trail Runner 2",
"attributes": [
{ "key": "Title", "value": "Trail Runner 2", "type": "single_line_text" },
{ "key": "Vendor", "value": "Acme", "type": "single_line_text" }
],
"draft_revision": 14,
"pending_publish": true,
"last_publish_status": "published",
"variant_ids": ["c410…", "c411…"],
"media_ids": ["88ab…"],
"stores": [
{ "store_id": "1e8e…", "store_domain": "acme.myshopify.com",
"pending_publish": true, "last_publish_status": "published" },
{ "store_id": "9c1f…", "store_domain": "acme-eu.myshopify.com",
"pending_publish": false, "last_publish_status": "published" }
]
}
These are the canonical values every store inherits, plus the
publish state per store. The same shape works for
/variants/{id}, /collections/{id}, and
/media/{id}.
5 · Compare a store's version
curl "https://api.peak-pim.com/api/v1/products/7b2a.../stores/9c1f..." \
-H "Authorization: Bearer $PEAK_PIM_KEY"
{
"store_id": "9c1f…",
"store_domain": "acme-eu.myshopify.com",
"revision": "sv1_<opaque token from the API>",
"attributes": [
{ "key": "Title", "value": "Trail Runner 2 — EU Edition", "type": "single_line_text" }
],
"pending_publish": false,
"last_publish_status": "published"
}
This is what that store actually publishes — overrides included. Diff it against
the canonical attributes from step 4 to find per-store drift, or use
/stores (no store_id) to fetch every store version at
once.
6 · Poll efficiently
Two mechanisms keep recurring syncs cheap:
-
updated_since— only entities whose canonical record changed since your last run. Store-local edits don't widen it, so re-read the store versions of entities you track. -
ETags — every GET 200 carries an
ETag; send it back asIf-None-Matchand unchanged resources answer304with no body.
curl -i "https://api.peak-pim.com/api/v1/products/7b2a..." \
-H "Authorization: Bearer $PEAK_PIM_KEY" \
-H 'If-None-Match: "W/abc123"'
# HTTP/2 304 — nothing changed, no body transferred
7 · Taste the operations surface
Beyond the catalog reads, v1 exposes markets, definitions, translations, sales channels, drops, and data health. Try the last one — the account's cached catalog-quality state:
curl "https://api.peak-pim.com/api/v1/data-health" \
-H "Authorization: Bearer $PEAK_PIM_KEY"
{
"dimensions": [
{ "dimension": "completeness", "scanned": true,
"result": { "…": "dimension-specific summary" },
"scanned_at": "2026-07-30T18:12:04Z" },
{ "dimension": "value-divergence", "scanned": false }
]
}
This read never scans: scanned: false means that dimension has never
been scanned — a different answer from "scanned, and clean". Recomputing a
dimension is POST /api/v1/data-health/scans, one of v1's first write
endpoints (pim:write) — see Data
health for scans, issue lists, and remediation batches.
8 · Create, edit a metafield, and publish
Use a key carrying pim:read, pim:write and pim:publish. This example uses jq and a store ID from step 2. Draft writes stay in Peak PIM; the final publish sends them to Shopify. Before setting mf:custom.material, confirm a Product metafield definition with namespace custom, key material and type single_line_text_field exists.
export PEAK_STORE_ID="9b2f6c1a-7d3e-4f80-b1c5-6a4e8d2b9c30"
# Create the product and its store version together.
jq -n --arg store "$PEAK_STORE_ID" '{
store_ids: [$store],
attributes: [
{key: "Internal Name", value: "Trail Runner 2", type: "single_line_text_field"},
{key: "Title", value: "Trail Runner 2", type: "single_line_text_field"}
]
}' > create-product.json
curl -sS --fail-with-body -X POST "https://api.peak-pim.com/api/v1/products" \
-H "Authorization: Bearer $PEAK_PIM_KEY" \
-H "Content-Type: application/json" \
--data-binary @create-product.json --output created-product.json
export PEAK_PRODUCT_ID=$(jq -r '.id' created-product.json)
# Read the store version to obtain its opaque revision.
curl -sS --fail-with-body \
"https://api.peak-pim.com/api/v1/products/$PEAK_PRODUCT_ID/stores/$PEAK_STORE_ID" \
-H "Authorization: Bearer $PEAK_PIM_KEY" --output store-version.json
jq '{revision: .revision, changes: {"mf:custom.material": "Cotton"}}' \
store-version.json > update-metafield.json
curl -sS --fail-with-body -X PATCH \
"https://api.peak-pim.com/api/v1/products/$PEAK_PRODUCT_ID/stores/$PEAK_STORE_ID" \
-H "Authorization: Bearer $PEAK_PIM_KEY" \
-H "Content-Type: application/json" \
--data-binary @update-metafield.json
# Publish explicitly to this store. This step writes to Shopify.
jq -n --arg store "$PEAK_STORE_ID" '{store_ids: [$store]}' > publish-product.json
curl -sS --fail-with-body -X POST \
"https://api.peak-pim.com/api/v1/products/$PEAK_PRODUCT_ID/publish" \
-H "Authorization: Bearer $PEAK_PIM_KEY" \
-H "Content-Type: application/json" \
--data-binary @publish-product.json --output publish-operation.json
export PEAK_OPERATION_ID=$(jq -r '.operation.id' publish-operation.json)
# Repeat this GET until terminal, then inspect each store outcome.
curl -sS --fail-with-body \
"https://api.peak-pim.com/api/v1/publish-operations/$PEAK_OPERATION_ID" \
-H "Authorization: Bearer $PEAK_PIM_KEY"
A 202 publish response means the operation was queued. Check each store’s outcome before considering the sync complete. On 409 conflict, read again and decide whether the edit still applies; do not blindly substitute current_revision. Product/variant creation has no idempotency key, so inspect the catalog after an uncertain create result before retrying.
For variants, create through POST /api/v1/products/{product_id}/variants, then use /api/v1/variants/{id} and its store subresources for editing and publication. See Variants for option prerequisites and parent-publication confirmation.