Skip to content
↑↓ navigate↵ openEsc close

Guides

Rate limits

Each API key may send 600 requests per minute.

Fixed window per key

  • The limit is counted per API key, not per tenant or IP. Several keys of one tenant each get their own budget.
  • Windows are fixed one-minute buckets aligned to the clock minute (UTC). The counter resets at the start of each minute.
  • Every authenticated request counts, including requests later rejected for scopes or validation.

Limits

CallerRequests per minute
API key600 per key
App token600 per installation
Sandbox mode60 per key or installation

Headers

HeaderSent onMeaning
x-ratelimit-limitevery API key responseRequests allowed per minute (600, or 60 in sandbox mode).
x-ratelimit-remainingevery API key responseRequests left in the current window.
retry-after429 onlySeconds until the window resets.

429 RATE_LIMITED

Over the limit, the API answers 429 with code RATE_LIMITED and does not run the request. details.retryAfter repeats the header value.

HTTP/1.1 429 Too Many Requests
x-ratelimit-limit: 600
x-ratelimit-remaining: 0
retry-after: 17
content-type: application/problem+json

{
  "type": "about:blank",
  "title": "RATE_LIMITED",
  "status": 429,
  "code": "RATE_LIMITED",
  "detail": "Too many requests for this API key",
  "details": {
    "retryAfter": 17
  },
  "instance": "/v1/orders?limit=200",
  "requestId": "6f1c2d3e-4b5a-4c6d-8e7f-90a1b2c3d4e5"
}

Retry and backoff

Wait retry-after seconds on 429. On 5xx and network errors, retry with exponential backoff and jitter. Do not retry other 4xx responses unchanged; they fail the same way again.

// Retries 429 (honouring retry-after) and 5xx with exponential backoff and jitter.
async function request(url, init = {}, attempts = 5) {
  for (let i = 0; ; i++) {
    const res = await fetch(url, init);
    const retryable = res.status === 429 || res.status >= 500;
    if (!retryable || i === attempts - 1) return res;
    const retryAfter = Number(res.headers.get('retry-after'));
    const backoff = Math.min(30, 2 ** i) * 1000 * (0.5 + Math.random() / 2);
    await new Promise((r) => setTimeout(r, retryAfter > 0 ? retryAfter * 1000 : backoff));
  }
}

Prefer limit=200 pages and webhooks over tight polling loops; one webhook replaces many list calls.