
429 Too Many Requests is the HTTP status a server returns when a client has sent more requests than its rate limit allows in a given time window. It is temporary and not a crash: wait the period in the Retry-After header (or back off exponentially if there is none), then send requests more slowly.
In short. 429 means a limit was crossed, and the useful part is the Retry-After header telling you when to try again. Receiving one is a client-side scheduling problem; sending one is a design choice about what you count, over what window, and per what — the last of which decides who gets caught unfairly.
The 429 Too Many Requests status is the server's signal: "you are too fast, slow down." It is the standard response from a rate limiter protecting APIs and websites from abuse. Below: who is actually at fault when you see it, how long it lasts, how a client should handle Retry-After, how to implement 429 correctly on the server, and what to do when your service receives 429 from an external API such as OpenAI or Spotify.
What 429 Means
Per RFC 6585 §4, 429 indicates the user has sent too many requests in a given time. Unlike 503 (service unavailable) or 403 (forbidden by policy), 429 is a temporary state — the client may retry after Retry-After.
The RFC says the response may include Retry-After; in practice it is the most important header in the response. Its value is either a number of seconds (Retry-After: 60) or an HTTP date (Retry-After: Wed, 21 Oct 2026 07:28:00 GMT) — see Retry-After on MDN. Recommended informal headers:
X-RateLimit-Limit— max requests per windowX-RateLimit-Remaining— remaining requestsX-RateLimit-Reset— Unix timestamp of window reset
The X- names are a convention, not a standard, and vendors vary: some send the reset as seconds-until-reset rather than a timestamp, OpenAI sends x-ratelimit-remaining-requests and x-ratelimit-remaining-tokens, and an IETF draft proposes standard RateLimit and RateLimit-Policy fields. Always read the provider's docs before parsing them.
Is error 429 my fault?
Usually not in the sense of "something is broken on your computer." A 429 is a decision made by the server (or a CDN/WAF in front of it) about the identity it is counting — and that identity is often shared. Typical reasons a normal visitor gets one:
- Shared IP address. Office networks, university Wi-Fi, mobile carriers that put many subscribers behind one address (carrier-grade NAT) and public Wi-Fi all look like one very busy client to a per-IP limiter. This is why a site can fail on home Wi-Fi yet load on mobile data: a different network is a different counter.
- Your own browser. Rapid refreshing, many tabs of the same site, an auto-refresh or price-tracking extension, or a page stuck in a redirect or retry loop all count as requests.
- Automation on your account. Scripts, integrations or third-party apps using your API key or login share its quota with you.
- Login and form endpoints. Sign-in, password reset and checkout pages usually have far stricter limits than the rest of the site, so a few failed attempts can trigger 429 there alone.
If you run the site and your visitors report 429, the fault is in the limit's design: counting per IP for everything catches whole offices; counting per user or per API key is fairer (see "Client Identification" below). On Cloudflare, the visitor-facing version of this is error 1015 “You are being rate limited”.
How long does a 429 error last?
As long as the limiter's window says — there is no universal duration. The only authoritative answer is in the response itself:
- Retry-After present — wait exactly that long. It is not a suggestion.
- No Retry-After, but
X-RateLimit-Resetor a vendor equivalent — wait until that time. - Neither — the duration depends on the algorithm behind it, which you can often infer from behavior.
| Limiter type | When requests work again | What makes it last longer |
|---|---|---|
| Fixed window (per minute/hour) | At the next window boundary | Nothing — the counter resets on schedule |
| Sliding window | When your oldest requests age out of the window | Retrying while blocked, if rejected requests are also counted |
Token bucket / nginx limit_req | Almost immediately, at the refill rate | Sending faster than the refill rate |
| Temporary ban (WAF rule, fail2ban, Cloudflare rate limiting rule) | When the ban/mitigation timeout set by the site owner expires | Some rules extend or re-trigger the ban on continued traffic |
| API quota (daily or monthly) | At the quota reset, or after upgrading the plan | Nothing short of the reset — retrying does not help |
How long to wait for 429 too many requests?
For a browser visitor: stop reloading and give it a few minutes, then try once. If it still fails after an hour, you are probably under a ban or quota, not a short window — contact the site. For code: honor Retry-After; without it, start at 1 second and double on each consecutive 429 with random jitter, capped at a minute or so, and give up after a fixed number of attempts. The worst option is an immediate tight retry loop: against a sliding window that counts rejected requests, it keeps you blocked indefinitely.
How to fix error 429 too many requests
The fix depends on which side of the request you are on. The common advice to "clear your cache and cookies" does nothing to a counter that lives on the server.
| You are | Likely cause | What actually fixes it |
|---|---|---|
| A visitor in a browser | Refreshing, many tabs, an extension, or a shared network IP | Stop reloading, close duplicate tabs, disable auto-refresh extensions, wait, sign in if the site offers higher limits to logged-in users, contact support if it persists |
| Writing a script or scraper | Too many requests per second or too many in parallel | Lower concurrency, add delays, honor Retry-After, cache responses, use the site's official API or bulk export if there is one, respect robots.txt |
| Calling a third-party API | Per-key rate limit or exhausted quota | Backoff with jitter, a client-side request queue, batching, caching, a higher plan or limit increase |
| Running the site or API | Your own nginx/CDN/WAF/app limit, or a 429 relayed from an upstream API | Find which layer sends it, then adjust what is counted and per whom (below) |
Find which layer is sending the 429
On a real stack, the 429 may come from the CDN, the web server, the application, or an upstream API your application calls and relays. The response headers and body usually give it away:
- Cloudflare —
server: cloudflareand acf-rayheader; a branded "rate limited" page. Check Security events in the dashboard for the matching rule. - nginx —
Server: nginxand nginx's plain built-in "429 Too Many Requests" page; a matchinglimiting requestsline in the error log. - Your application — a JSON body and
X-RateLimit-*headers you set yourself. - An upstream API — nothing in your own limiter logs; the 429 shows up in your application's outbound request logs first.
# Status and headers of a single request
curl -s -o /dev/null -D - https://example.com/api/endpoint
# Fire 30 requests and count the status codes you get back
for i in $(seq 1 30); do
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/api/endpoint
done | sort | uniq -cgrep "limiting requests" /var/log/nginx/error.log | tail -n 20
# ... [error] ... limiting requests, excess: 20.500 by zone "api", client: 203.0.113.7, ...
429 Too Many Requests on OpenAI, ChatGPT, Spotify, Etsy and the Internet Archive
Many searches for 429 name a specific service. The mechanics are the same everywhere, but a few have quirks worth knowing:
| Service | What the 429 usually means | What to do |
|---|---|---|
| OpenAI API | Either a rate limit (requests or tokens per minute) or an exhausted quota — the error body says which (rate_limit_exceeded vs insufficient_quota) | Rate limit: back off and read the x-ratelimit-* headers. Quota: retrying never helps — check billing and usage limits. See OpenAI rate limits |
| ChatGPT (web app) | Too many messages or requests from your account in a short period | Wait and try later; it is a per-account limit, not a problem with your browser |
| Spotify Web API | Your app exceeded its rolling-window limit | Wait the Retry-After seconds, batch calls where endpoints accept multiple IDs. See Spotify rate limits |
| Etsy and other storefronts | Anti-abuse limiting on your IP or session — often a shared network or an extension | Stop reloading, wait, try another network you legitimately use, contact support if it persists |
| Internet Archive / Wayback Machine | Too many page loads or Save Page Now captures in a short time | Space out requests, run bulk jobs sequentially rather than in parallel, and wait before resuming |
For LLM APIs specifically, rate limits and latency deserve their own monitoring — see monitoring LLM APIs: latency, cost, uptime.
Rate Limiting Strategies
Token Bucket
A bucket of tokens refills at a fixed rate. Each request consumes a token. Empty = 429.
Leaky Bucket
A fixed-size queue drained at a constant rate. Overflow = 429.
Fixed Window
Counter per minute/hour. Simple, but allows bursts at window boundaries.
Sliding Window
Hybrid: accounts for the previous window proportionally. The smoothest approach.
A deeper comparison with trade-offs for each algorithm is in rate limiting strategies for web APIs.
nginx Configuration
# Zone: 10MB fits ~160k unique IPs
limit_req_zone $binary_remote_addr zone=api:10m rate=60r/m;
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
server {
location /api/ {
limit_req zone=api burst=20 nodelay;
limit_req_status 429;
proxy_pass http://backend;
}
location /api/auth/login {
limit_req zone=login burst=3 nodelay;
limit_req_status 429;
proxy_pass http://backend;
}
}Parameters:
rate=60r/m— 1 request per second on averageburst=20— allow bursts up to 20, drop the restnodelay— do not delay, return 429 immediately on overflowlimit_req_status 429— needed because nginx rejects with 503 by default, which clients and crawlers read as a server outage rather than "slow down"
nginx does not add Retry-After on its own. To send it together with a JSON body, route rejected requests to a named location:
location /api/ {
limit_req zone=api burst=20 nodelay;
limit_req_status 429;
error_page 429 = @ratelimited;
proxy_pass http://backend;
}
location @ratelimited {
add_header Retry-After 60 always;
default_type application/json;
return 429 '{"error":"too_many_requests"}';
}Directive reference: ngx_http_limit_req_module. On Apache, note that mod_ratelimit limits bandwidth per connection, not the number of requests — request-count limiting there needs a separate module or a limiter in front.
Application-Level Rate Limiting (Redis sliding window)
<?php
function checkRateLimit(Redis $redis, string $key, int $max, int $window): array {
$now = microtime(true);
$pipe = $redis->multi();
$pipe->zRemRangeByScore($key, 0, $now - $window);
$pipe->zAdd($key, $now, "$now:" . bin2hex(random_bytes(4)));
$pipe->zCard($key);
$pipe->expire($key, $window * 2);
$results = $pipe->exec();
$count = $results[2];
return [
'allowed' => $count <= $max,
'limit' => $max,
'remaining' => max(0, $max - $count),
'reset' => time() + $window,
];
}
// Usage
$rl = checkRateLimit($redis, "rl:user:{$userId}", 60, 60);
header("X-RateLimit-Limit: {$rl['limit']}");
header("X-RateLimit-Remaining: {$rl['remaining']}");
header("X-RateLimit-Reset: {$rl['reset']}");
if (!$rl['allowed']) {
http_response_code(429);
header('Retry-After: 60');
header('Content-Type: application/json');
echo json_encode(['error' => 'Too many requests']);
exit;
}One design choice to be aware of: this function adds the request to the set before checking the count, so rejected requests also count. A client that keeps hammering stays blocked until it actually slows down — usually what you want for abuse, but harsh on a buggy client in a retry loop. If you prefer to count only accepted requests, check zCard first and add the entry only when allowed.
Client-side 429 Handling (exponential backoff)
async function fetchWithRetry(url, options = {}, maxRetries = 5) {
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 waitMs = retryAfter
? parseInt(retryAfter, 10) * 1000
: Math.min(1000 * Math.pow(2, attempt), 60000);
console.warn(`429 received, retrying in ${waitMs}ms`);
await new Promise(r => setTimeout(r, waitMs));
}
throw new Error('Rate limit exceeded after retries');
}Never retry aggressively without backoff — it worsens the rate limit and may trigger a WAF ban.
Two refinements the snippet above skips. parseInt on an HTTP-date Retry-After returns NaN, and many clients retrying on the same exponential schedule return at the same moment. Parse both formats and add jitter:
function retryAfterMs(header) {
if (!header) return null;
const seconds = Number(header);
if (Number.isFinite(seconds)) return seconds * 1000; // "120"
const date = Date.parse(header); // "Wed, 21 Oct 2026 07:28:00 GMT"
return Number.isNaN(date) ? null : Math.max(0, date - Date.now());
}
// Full jitter: spread retries so clients do not return in lockstep
function backoffMs(attempt, capMs = 60000) {
return Math.random() * Math.min(capMs, 1000 * 2 ** attempt);
}In Python, requests can delegate retries to urllib3, which honors Retry-After by default. Note that it only retries idempotent methods unless you change allowed_methods, so POSTs are not replayed silently:
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
retry = Retry(
total=5,
backoff_factor=1, # 0s, 2s, 4s, 8s... between attempts
status_forcelist=[429, 503],
respect_retry_after_header=True,
)
session = requests.Session()
session.mount("https://", HTTPAdapter(max_retries=retry))
resp = session.get("https://api.example.com/items")Client Identification: IP, User, API Key
The right tracker depends on context:
- APIs with keys — limit per API key
- Login endpoints — per IP (prevents brute force)
- User actions — per user_id (so NAT does not block many users sharing one IP)
// Hierarchy: user ID > API key > IP
function getRateLimitKey(Request $req): string {
if ($req->user?->id) return "user:{$req->user->id}";
if ($req->apiKey) return "apikey:{$req->apiKey}";
return "ip:{$req->ip}";
}Behind a proxy or CDN, make sure the "IP" is the real client address (for example via nginx's real_ip module with trusted proxy ranges) — otherwise every visitor shares the CDN's address and one limit.
Getting 429 from External API: What to Do
- Read
Retry-After— it is not a suggestion, it is a requirement. - Use a circuit breaker — temporarily stop hitting the degrading API.
- Cache responses in Redis with a sensible TTL.
- Batch requests when the API supports it.
- Request a limit increase — paid tiers usually offer more.
Do not pass an upstream 429 straight to your own users as if they had done something wrong: either queue and retry server-side, or return your own error that explains the dependency is throttled.
To monitor your API availability, use Enterno.io Monitors with expected_code=200 — detect 429 regressions instantly.
429 vs 503 vs 403
| Code | When to Use | Who is affected | Retry makes sense? |
|---|---|---|---|
| 429 | Client exceeded rate limit | One client, key or IP | Yes, after Retry-After |
| 503 | Server overloaded (general, not client-specific) | Everyone | Yes, later |
| 403 | Access policy (client fully blocked) | One client or a group | No, until the policy changes |
Details on the neighbors: HTTP 503 Service Unavailable and HTTP 403 Forbidden.
How to check your site or API for 429s
- Run the HTTP headers checker against the endpoint to see the exact status code and whether
Retry-AfterandX-RateLimit-*headers come back. - Set up uptime monitors with an expected status of 200 so a limit that starts catching real users or your own health checks shows up as an incident, not a support ticket.
- If a CDN or WAF is involved, run the security headers scan to confirm which layer answers first.
FAQ
How to get rid of too many requests?
Slow down: wait out the Retry-After period, reduce parallel requests, cache what you already fetched, and ask the provider for a higher limit if you genuinely need it. Rotating IPs or keys to evade the limit is a terms-of-service violation and a ban risk.
Does clearing the browser cache fix a 429?
No. The counter lives on the server or CDN and is keyed to your IP, session or account. Waiting, reloading less often and switching off auto-refresh extensions are what help.
Why do I get 429 on my very first request?
Because the limit is counted per shared identity — an office, campus or mobile-carrier IP where other people have already used up the allowance — or because the endpoint has a very strict limit, such as login.
Does 429 affect SEO?
For Googlebot — yes, if frequent. Googlebot reduces crawl rate, and URLs that keep failing can drop out of the index; see Google's guidance on HTTP errors. Honor Retry-After and never block official Googlebot IPs.
What is an adequate API rate limit?
It depends. Typical values: 60 req/min for general APIs, 5 req/min for login, 10 req/sec for realtime endpoints.
Conclusion
429 protects your service and partners. Configure a proper rate limit (sliding window + Redis), always return Retry-After and X-RateLimit-* headers, and use exponential backoff on the client. To monitor your API endpoints, use Enterno.io.