Rate limits
Limits are counted per key rather than per address, so your throughput is yours alone and is not affected by anyone else integrating from the same network.
The limits
Each endpoint has its own budget, measured over a rolling minute. Reads are generous; the writes that spend credit are deliberately tighter.
| Endpoint | Limit |
|---|---|
GET /catalogue | 120/min |
GET /orders | 120/min |
POST /orders | 30/min |
GET /orders/{id} | 120/min |
POST /orders/{id}/cancel | 30/min |
POST /quotes | 60/min |
GET /quotes/{id} | 120/min |
POST /uploads | 60/min |
Failed authentication | 20/min |
Failed authentication is counted by source address rather than by key - a request that did not authenticate has no key to count against.
Handling a 429
Over the limit you get 429 with error.code = "RATE_LIMITED" and a Retry-After header in seconds. Honour it: it is computed from the actual window, so it will always beat a guess.
async function callWithRetry(path: string, init: RequestInit, attempt = 0): Promise<unknown> {
const res = await fetch(API + path, init);
if (res.status === 429 && attempt < 5) {
// Retry-After is in seconds and is authoritative - prefer it to your own
// backoff curve, which cannot know how full the window is.
const wait = Number(res.headers.get("Retry-After") ?? 5);
await new Promise((r) => setTimeout(r, wait * 1000));
return callWithRetry(path, init, attempt + 1);
}
return res.json();
}There are currently no X-RateLimit-* headers, so you cannot see how much budget is left before you spend it. If you are running a bulk job, pace it against the table above rather than sprinting into a 429.
One retry rule worth repeating: if a POST /orders is throttled, retry with the same Idempotency-Key. A fresh key on a retry is how an order gets placed twice.