Concepts
Rate limits
Per-key request limits, the X-RateLimit headers and handling 429 answers.
Each API key may make a set number of requests per minute (a sliding one-minute window). The limit depends on
the plan; it is always in the X-RateLimit-Limit header, so read it instead of hard-coding a number.
Every authenticated answer carries:
| Header | Meaning |
|---|---|
X-RateLimit-Limit |
Requests this key may make per minute. |
X-RateLimit-Remaining |
Requests left in the current window. |
X-RateLimit-Reset |
Unix time (seconds) when a request is next freed. |
Over the limit, the API answers 429 with code rate_limited and a Retry-After header (seconds). Refused
requests don't count against the limit.
{
"error": {
"type": "rate_limit",
"code": "rate_limited",
"message": "Too many requests for this key. Wait and try again.",
"param": null,
"request_id": "req_4f2a9c0d1e7b3a5c6d8e9f01"
}
}Handling 429
async function request(url, options, attempt = 0) {
const response = await fetch(url, options);
if ((response.status === 429 || response.status === 503) && attempt < 5) {
const wait = Number(response.headers.get("Retry-After") ?? 2 ** attempt);
await new Promise((resolve) => setTimeout(resolve, (wait + Math.random()) * 1000));
return request(url, options, attempt + 1);
}
return response;
}import random
import time
import requests
def request(method, url, attempts=5, **kwargs):
for attempt in range(attempts):
response = requests.request(method, url, timeout=30, **kwargs)
if response.status_code not in (429, 503):
return response
wait = float(response.headers.get("Retry-After", 2 ** attempt))
time.sleep(wait + random.random())
return responseOther limits
- Invalid keys: more than 20 failed authentications in a minute from one address block that address for 15
minutes (
429 too_many_invalid_keys, withRetry-After). - Limiter unavailable: if Ordstage briefly can't check limits, the API answers
503 rate_limiter_unavailablewithRetry-After: 5rather than run unlimited. Retry after the wait. - Request size: request bodies are limited to 10 MB (
413).
To stay well inside the limits, use webhooks instead of polling, and spread bulk jobs over time.