TroubleshootingTroubleshooting
429 and retry behaviour
How rate limiting works and how to back off correctly.
By the end: You can handle 429 responses with the right delay and jitter.
A 429 means the request was refused before reaching a model, so it costs nothing.
Reading the response
| Field | Value | Notes |
|---|---|---|
| retry-after | Seconds | Wait at least this long before retrying. |
| error.code | rate_limit_exceeded | A rate or concurrency limit was hit. |
| error.code | quota_exceeded | A daily or monthly quota is exhausted; retrying soon will not help. |
Backoff that works
Pattern
let attempt = 0;
for (;;) {
const res = await send();
if (res.status !== 429) return res;
const wait = Number(res.headers.get('retry-after') ?? 2 ** attempt);
const jitter = wait * 0.25 * Math.random();
await new Promise((r) => setTimeout(r, (wait + jitter) * 1000));
if (++attempt > 5) throw new Error('still rate limited');
}Retrying
quota_exceeded immediately just burns time. It resets at the start of the next UTC day or month.