Handling 429 responses gracefully
3 min read Updated October 22, 2026
What a 429 is
HTTP 429 means you exceeded a rate-limit window. The response has a Retry-After header (seconds) and this body:
json
{
"error": "rate_limit_exceeded",
"message": "Rate limit exceeded: 300 requests per minute. Retry after 12s.",
"retryAfter": 12,
"limit": 300,
"window": "minute",
"environment": "production"
}Backoff with jitter
javascript
async function callWithBackoff(url, options, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const res = await fetch(url, options);
if (res.status !== 429) return res;
const retryAfter = res.headers.get('Retry-After');
const baseWait = retryAfter ? Number(retryAfter) * 1000 : Math.pow(2, attempt) * 1000;
await new Promise(r => setTimeout(r, baseWait + Math.random() * 500));
}
throw new Error('Rate limit exceeded after max retries');
}Nothing retries for you. Retry only 429 and 5xx responses, and never retry a 4xx validation error.
Proactive limiting
Watch X-RateLimit-Remaining and throttle before it reaches zero.
Common causes
- Tight loops with no delay
- Parallel workers sharing one key
- Retry storms after a failure
- The sandbox limit of 60 requests a minute (production allows 300)
Was this article helpful?