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
| Caller | Requests per minute |
|---|---|
| API key | 600 per key |
| App token | 600 per installation |
| Sandbox mode | 60 per key or installation |
Headers
| Header | Sent on | Meaning |
|---|---|---|
x-ratelimit-limit | every API key response | Requests allowed per minute (600, or 60 in sandbox mode). |
x-ratelimit-remaining | every API key response | Requests left in the current window. |
retry-after | 429 only | Seconds 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.