Developers

API fundamentals

Markdown

Rate limits

How many requests your app can send, and how to handle 429 and 503 without losing work.

Limits

Each request passes through several limits, and the first one exceeded returns 429 RATE_LIMITED:

LimitCapacityRefill
Your app on one store, all requests20 requests5 per second
Your app on one store, writes5 requests1 per second
Your app across all stores100 requests50 per second
One store, across all apps40 requests20 per second
Per access token600 per minute
Per IP address1200 per minute on the API, 600 on /oauth/*

Limits use a token bucket: you can send a burst up to the capacity once, then the bucket refills at the refill rate. An app that sends 4 requests per second to one store never sees a 429.

Handling 429

HTTP
HTTP/1.1 429 Too Many Requests
Retry-After: 1

Wait for the number of seconds in Retry-After, then resend the same request. For writes, resend with the same Idempotency-Key so the effect is not applied twice.

Node.js
async function call(url, init, attempt = 0) {
  const response = await fetch(url, init)
  if ((response.status === 429 || response.status === 503) && attempt < 5) {
    const wait = Number(response.headers.get('retry-after') ?? 1) * 1000 * 2 ** attempt
    await new Promise(resolve => setTimeout(resolve, wait + Math.random() * 250))
    return call(url, init, attempt + 1)
  }
  return response
}

Handling 503

CodeMeaningWhat to do
STORE_MIGRATINGThe store is moving between serversRetry after a few seconds
POLICY_UNAVAILABLEThe app policy could not be verified right nowRetry after a second
APPS_DISABLEDReads or writes are temporarily disabled by LARMRetry later, without rapid retries
APPS_SCOPE_RUNTIME_NOT_READYThis resource is not enabled on this merchant's store yetDo not retry immediately; retry after a few hours

Best practices

  • Subscribe to webhooks instead of polling. The order.created webhook reaches you within seconds, while polling the API every minute consumes your limit on every store.
  • For periodic sync, use updated_after to fetch only what changed.
  • Spread heavy jobs across stores over time, rather than hitting every store at once at midnight.

    ↑↓ moveEnter openEsc close