Shopify Store API — Getting Started — FetchLayer Docs
Documentation menu
FetchLayer FetchLayer API

Getting Started

You supply the domain. Confirm it is a Shopify store, then read its catalog — the same two steps for every storefront on the web.

1. Get your API key

Create an account and generate a FetchLayer API key from your dashboard. There is no Shopify Partner account to register, no app to install on the store, and no per-store access token.

Create free account →

2. Confirm the domain is a Shopify store

bash
curl -X POST https://api.fetchlayer.dev/shopify-stores/detect-store \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"store":"allbirds.com"}'

A domain that is not a Shopify store is a normal 200 with isShopify: false, not an error — so this is safe to run down a list of a thousand domains. Read the endpoints array on the response before going further: it says whether this particular store publishes its products, its collections and its metadata, and a store can switch any of them off.

Tip

A store that looks nothing like Shopify is usually still readable. Every classic storefront answers the same endpoints no matter how it has been themed, and a headless store is read at the permanentDomain its own markup reveals — pass that back as store.

3. Pull the catalog

bash
curl -X POST https://api.fetchlayer.dev/shopify-stores/products \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"store":"allbirds.com","limit":250,"pages":5}'

Each page walked counts as one request, so pages: 5 bills as five. When hasNextPage is true, pass the response's nextCursor back as cursor to continue where you stopped.

Read the notes array

Every result carries a notes array, and it is the only place a partial answer announces itself — a walk that stopped early, an upstream limit, a field this store does not publish. An empty notes means the result is as complete as the store publishes.

4. Go deeper on one product

A catalog walk returns every variant's price, sale price, SKU and availability, but not units on hand. /product-detail is the only endpoint that returns inventoryQuantity, and only for stores that track inventory — which is what makes per-product stock tracking over time possible.

bash
curl -X POST https://api.fetchlayer.dev/shopify-stores/product-detail \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"product":"https://allbirds.com/products/mens-tree-runner-nz-natural-white"}'

Tip

Have a URL and are not sure what it points at? Send it to /resolve-url first — it tells you whether it is a product, a collection or the store, and hands back the domain, handle and variant id the other endpoints take. It touches no store and does not consume a credit.

Error responses

Every error, on every route, is JSON in one shape: error (what went wrong, in words), status, code, retryable and requestId, which also comes as the X-Request-Id header. Branch on code and retryable rather than on the text; on a 5xx the text is generic. Quote requestId when reporting a problem.

json
{
  "error": "Missing required parameter: url",
  "status": 400,
  "missing": ["url"],
  "code": "invalid_input",
  "retryable": false,
  "requestId": "922874e3-c0d0-40d8-8866-d198af132420"
}
codeStatusRetryableMeaning
invalid_input400noThe request needs fixing; error names the field. A missing required parameter also lists it in missing.
unauthorized401noThe API key is missing or not valid.
insufficient_credits402noNothing left to spend: no credits, a used-up free trial, or a monthly allowance and credit balance both spent. error says which. Waiting does not help; topping up does.
forbidden403noThis key or account is not allowed this request.
not_found404noWhat the request names does not exist or is not public.
expired410noWhat the request refers to is no longer available; ask for it afresh.
quota_exceeded429noA request-rate limit was reached; error says which. Slow down before sending more.
cancelled499noThe caller closed or cancelled the request before it finished. The work was stopped and nothing was charged.
time_limit503yesYour timeoutMs ran out before there was anything to return; ask again, or allow more time.
temporarily_unavailable503yesNot available right now; ask again shortly.
store_unreachable503yesStore routes (Shopify, WooCommerce): the store itself can’t be reached right now — it may be offline. Ask again later. Where a store route doesn’t say so yet, an offline store is temporarily_unavailable.
upstream_error502yesThe source answered in a way that could not be used; ask again later.
internal_error500yesSomething failed on our side; ask again later, and quote requestId if it persists.
tool_error—noMCP only: a tool call failed in a way no other code describes; error says what.

More codes may be added; treat one you do not recognise by its retryable flag. Over MCP, a tool error carries the same fields in structuredContent, and as a JSON text block beside the message.

Next steps