429 Too Many Requests: What It Means and How to Fix It
A 429 Too Many Requests response means you've hit a rate limit: too many requests in too short a time. How to read the Retry-After and rate-limit headers, how to retry properly with backoff, the usual causes (loops, missing caching, shared IPs, AI API limits), and how to add rate limits to your own app.
429 Too Many Requests means you've hit a rate limit. The server is fine; it's just refusing to handle more requests from you until some time has passed.
You'll see it from APIs (OpenAI, Anthropic, GitHub, Stripe), from login pages that block repeated attempts, and from services like Cloudflare protecting a site.
Read the headers first
A well-behaved 429 tells you when to try again:
HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1760000000
Retry-After— wait this many seconds (or until this date).X-RateLimit-*(names vary by API) — your limit, what's left, and when it resets.
The fix: wait and retry properly
Retrying immediately in a loop makes things worse — you burn more of the limit and may get blocked for longer. Retry with exponential backoff: wait 1 second, then 2, then 4, plus a little randomness.
async function fetchWithRetry(url, options, attempts = 5) {
for (let i = 0; i < attempts; i++) {
const res = await fetch(url, options)
if (res.status !== 429) return res
await res.body?.cancel()
if (i === attempts - 1) break
const header = res.headers.get('retry-after')
const retryAfter = header === null ? NaN
: /^\d+$/.test(header.trim()) ? Number(header) * 1000
: Date.parse(header) - Date.now()
const wait = Number.isFinite(retryAfter)
? Math.max(0, retryAfter)
: 2 ** i * 1000 + Math.random() * 500
await new Promise(r => setTimeout(r, wait))
}
throw new Error('Still rate limited after retries')
}
This handles both seconds and an HTTP date in Retry-After. Use it for retryable rate limits; inspect the API's error body first when 429 can also mean a billing or quota failure. Retry writes only when the API guarantees they are safe to repeat, for example with an idempotency key. Most official SDKs (Anthropic, OpenAI, Stripe) already have retry handling. (Exponential Backoff and Jitter)
Why you're hitting the limit
- A loop or re-render firing requests over and over — classic in React when a
useEffecthas the wrong dependencies. (React useEffect Explained) - No caching: fetching the same data on every page load. (Caching Explained)
- Doing things one at a time that the API lets you batch.
- Too much parallelism: firing 200 requests at once with
Promise.all. - Shared IP: on a shared host or CI runner, other people's traffic counts against the same IP.
- Your plan's limit is just low. New API accounts often start on low tiers that rise as you spend.
AI APIs: 429 isn't always the same 429
- Anthropic: a 429 is a rate limit on your organisation's tier; a 529 means the API is overloaded for everyone. (Claude Code 529 Overloaded)
- OpenAI: a 429 can also mean exhausted credit or a hard spending/usage limit, sometimes reported as
insufficient_quotaor a more specific billing code. Resolve the billing or limit issue before retrying. (You Exceeded Your Current Quota)
Adding rate limits to your own app
You should send 429s too. Rate-limit logins, signups, password resets and anything that calls a paid AI API, or bots will brute-force accounts and run up your bill. (Implement Rate Limiting, Stop Bots Running Up Your AI Bill)
The summary
- 429 = rate limited. Respect
Retry-After. - Retry with exponential backoff, not in a tight loop.
- Fix the cause: loops, missing caching, too much parallelism, or a low tier.
EasySpawn gives your app a server-side backend where API calls can be cached, queued and rate-limited properly — with Claude Code to add the retry logic. See how it works or join the waitlist.
Related: What Is Rate Limiting? · HTTP Status Codes Explained · Message Queues Explained · Load Testing Your App
Keep reading
Cannot Set Headers After They Are Sent to the Client: How to Fix It
"Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client" means your Express or Node.js code tried to send a second response to one request. The common causes — a missing return, callbacks and loops that respond twice, next() after res.send(), async errors — and how to fix each.
HTTP Headers Explained: The Hidden Part of Every Request
Every web request and response carries headers — extra information about the content, the client, authentication, caching and security. The headers you'll meet most (Content-Type, Authorization, Cookie, Cache-Control, CORS), how to see them in DevTools, and how to set them.