429 Too Many Requests
Client Error
The client has sent too many requests within a given time window and the server is throttling. 429 should include a Retry-After header, either as a number of seconds or an HTTP-date, telling the client when it is safe to retry. Many APIs also include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers to expose the current quota state before clients hit the cap. Rate limiting is the right tool for fairness and abuse prevention; outright bans should use 403 instead.
When does this happen?
Return 429 when the caller has exceeded a rate limit. Requests per minute, requests per hour, requests per second, or some token-bucket equivalent. Common triggers include aggressive scrapers, runaway client scripts, malformed retry loops without backoff, and legitimate clients that simply burst harder than their plan allows. The Retry-After header is essential: without it, well-behaved clients have to guess when to retry, and most fall back to exponential backoff that may be more conservative than necessary. APIs typically rate-limit per API key or per IP, sometimes both. The 429 response body can include details about which limit was hit and which plan tier would lift it. Browsers do not surface 429 specifically; the client code must handle it, ideally with exponential backoff plus jitter. Watch for cascading 429s during incident response. If you scale down to save costs and your clients all retry at once, you can wedge yourself into a permanent overload.
Common causes
- Client exceeded its per-minute or per-hour API quota.
- Automated scraper hitting the site without rate limiting or respect for Crawl-Delay in robots.txt.
- Misbehaving client retrying a failed request in a tight loop without exponential backoff.
- Burst of requests from a single IP. Common during a botnet attack or a misconfigured deployment that fanned out the same call thousands of times.
- Cloudflare or Akamai bot-protection rule triggered by unusual request patterns.
- Service-level abuse rule. E.g. login endpoint rate-limits per IP to slow down password-stuffing attacks.
- Server's request queue saturated and the load balancer is shedding excess load with 429.
- API key shared across too many concurrent consumers, exceeding the per-key limit.
How to fix it
- Read the Retry-After header and wait that long before retrying. It is the server's explicit hint.
- Implement exponential backoff with jitter. Start at a small delay, double on each retry, and cap at a sensible maximum.
- Inspect X-RateLimit-Remaining (or your provider's equivalent) on every response so you can throttle proactively rather than reactively.
- Cache responses where possible. Repeated identical requests are the most common cause of avoidable rate-limit hits.
- Batch requests if the API supports it. One bulk call costs less quota than fifty individual ones.
- Upgrade your plan if the limits are too low for legitimate use. The response often includes a hint about plan tiers.
- Distribute requests over time. If your job needs to call an API 10,000 times, spread the calls over the rate-limit window rather than firing them in a burst.
- For login endpoints, slow down or back off after the first 429. Repeated attempts likely look like an attack.
Real-world examples
- Script polls an API every second when the rate limit is 60 requests per minute and the script has bugs that occasionally retry within the same second.
- Server returns 429 with Retry-After: 5 once the limit is hit. The script must add backoff or slow polling.
- Login form is hit with credential-stuffing attempts.
- Server returns 429 after the third failed attempt within 60 seconds. Attackers slow down or move on; legitimate users see a CAPTCHA.
- Cron job runs in parallel by accident and 50 instances of the same script hit the API simultaneously.
- Server returns 429 to most of them with Retry-After: 30. Operator fixes the cron concurrency.
- Browser tab makes hundreds of fetch calls when a user types in a search box without debouncing.
- Server returns 429. UI surfaces a "Slow down" message; developer adds debouncing to the input handler.
- Third-party integration on a free plan hits 1000 requests per day and a Friday-evening batch exceeds the quota.
- API returns 429 with a hint to upgrade. Integration backs off and resumes the next day, or operator upgrades the plan.
- CDN/WAF (Cloudflare, Akamai) detects bot-like patterns and rate-limits the IP.
- Server returns 429 even though the application itself would not have rate-limited. Operator reviews bot rules and either tightens them or adds an allow-list.
Debugging
- Run curl -I -H 'Authorization: Bearer <token>' https://api.example.com/v1/anything and check X-RateLimit-Remaining and X-RateLimit-Reset (or the API's equivalents).
- Inspect the Retry-After header in the 429 response. It tells you exactly when retry will succeed.
- Capture the request rate from your own logs. Many 429s come from spikes you did not realize were happening.
- Confirm whether the 429 is from your application or from a CDN/WAF. Cloudflare's cf-ray header and similar tells you the rate limit fired at the edge.
- Test from a different IP to rule out IP-based rate limiting; if it succeeds, your origin IP was the trigger.
How it differs from related codes
HTTP 503
503 means the server is unavailable due to overload or maintenance. 429 means the server is fine but is throttling you specifically. Retry-After is meaningful on both, but the cause is different.
HTTP 403
403 is a permission-based denial. 429 is a quota-based denial that resolves on its own after the rate-limit window. If a client should be permanently denied, use 403, not 429.
HTTP 509
509 (Bandwidth Limit Exceeded, non-standard) is similar in spirit but specifically about bandwidth quotas. 429 is the standard for request-rate limiting.
Related status codes
400 401 403 408 500 502 503 504
See HTTP 429 in your redirect chains?