---
title: "429 Too Many Requests"
url: https://erroneo.us/developers/http-429-too-many-requests/
description: "HTTP 429 means you hit a rate limit. What RFC 6585 says, how to honor Retry-After, exponential backoff with jitter in code, and the new RateLimit headers draft."
type: guide
section: "Developers"
date_modified: 2026-10-20
publisher: erroneo.us
license: All rights reserved. Quote with attribution and a link.
---

# 429 Too Many Requests

> A 429 is the politest error in HTTP: it does not say you are wrong, only that you are early. Handling it well is mostly about waiting the right amount of time, and not all retrying at once.

Rate limits protect servers from being overwhelmed, whether by an abusive scraper, a buggy loop, or a perfectly legitimate customer who suddenly runs a big batch job. The status code that says "you have hit the limit" is `429 Too Many Requests`. Most 429 incidents are not caused by the limit itself but by what clients do next: retry immediately, retry in lockstep, or ignore the hint telling them when to come back.

## What the spec says

429 is not in the original HTTP/1.1 specs. It was added by [RFC 6585 §4](https://www.rfc-editor.org/rfc/rfc6585#section-4) in 2012, which says it "indicates that the user has sent too many requests in a given amount of time ('rate limiting')." The RFC adds three points worth knowing:

- The response "SHOULD include details explaining the condition, and MAY include a Retry-After header indicating how long to wait."
- The RFC "does not define how the origin server identifies the user, nor how it counts requests." Limits can be per IP, per API key, per account, per endpoint, or shared across a cluster.
- "Responses with the 429 status code MUST NOT be stored by a cache."

Its [security considerations](https://www.rfc-editor.org/rfc/rfc6585#section-7.2) also make a practical point: answering every request with a 429 still costs resources, so under real attack "it may be more appropriate to just drop connections." In other words, servers are not required to send 429, and a client that is hammering a server may see timeouts instead.

## Retry-After

[RFC 9110 §10.2.3](https://www.rfc-editor.org/rfc/rfc9110#name-retry-after) defines `Retry-After` as either an HTTP date or a number of seconds:

```
HTTP/1.1 429 Too Many Requests
Retry-After: 120
Content-Type: application/problem+json

{"type": "about:blank", "title": "Too Many Requests", "status": 429,
 "detail": "Limit of 100 requests per minute exceeded for this API key."}
```

```
Retry-After: Fri, 31 Dec 1999 23:59:59 GMT
```

Your client must handle both forms. A surprising number of hand-written clients parse only the integer, crash on the date form, and then fall back to retrying immediately.

## The RateLimit headers draft

For years APIs have invented their own quota headers (`X-RateLimit-Remaining` and friends; GitHub, for example, uses `x-ratelimit-remaining` and [sets it to 0 when you are limited](https://docs.github.com/en/rest/using-the-rest-api/troubleshooting-the-rest-api)). The IETF HTTP API working group is standardising this in [draft-ietf-httpapi-ratelimit-headers](https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/), at version 11 as of May 2026 and still an Internet-Draft rather than an RFC. It defines two fields:

| Field | Parameters | Meaning |
|---|---|---|
| `RateLimit-Policy` | `q` quota, `w` window in seconds, optional `qu` unit and `pk` partition key | The rules: for example 100 requests per 60 seconds |
| `RateLimit` | `r` remaining quota, `t` seconds until the window resets, optional `pk` | Where you stand right now |

```
HTTP/1.1 200 OK
RateLimit-Policy: "basic";q=100;w=60
RateLimit: "basic";r=20;t=56
```

Those values come from the draft's own examples: 20 requests remain in a 100-per-minute policy, with 56 seconds left in the window. The draft says that if a response carries both fields, "the Retry-After field MUST take precedence." Because it is still a draft, treat these headers as useful hints and keep your client working when they are absent.

## Backoff with jitter

When there is no `Retry-After`, the standard approach is exponential backoff: wait a little, then twice as long, then twice as long again, up to a cap. The refinement that matters is *jitter*, a random element in each delay. Without it, every client that failed at the same moment retries at the same moment, and the spike repeats. In his widely cited AWS post [Exponential Backoff And Jitter](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/), Marc Brooker simulated contending clients and found jittered backoff cut the total call count by more than half compared with plain exponential backoff, concluding that it should be "considered a standard approach for remote clients." His "full jitter" variant picks a random delay between zero and the current exponential ceiling.

### JavaScript

```
function retryAfterMs(res) {
  const h = res.headers.get("Retry-After");
  if (!h) return null;
  const secs = Number(h);
  if (Number.isFinite(secs)) return Math.max(0, secs * 1000);
  const when = Date.parse(h);               // HTTP-date form
  return Number.isNaN(when) ? null : Math.max(0, when - Date.now());
}

async function fetchWithBackoff(url, opts = {}, { tries = 5, base = 500, cap = 30000 } = {}) {
  for (let attempt = 0; ; attempt++) {
    const res = await fetch(url, opts);
    if (res.status !== 429 && res.status !== 503) return res;
    if (attempt + 1 >= tries) return res;   // give up, let the caller decide
    const hinted = retryAfterMs(res);
    const ceiling = Math.min(cap, base * 2 ** attempt);
    const delay = hinted ?? Math.random() * ceiling;   // full jitter
    await new Promise(r => setTimeout(r, delay));
  }
}
```

### Python

```
import random, time
from email.utils import parsedate_to_datetime
from datetime import datetime, timezone
import requests

def retry_after_seconds(resp):
    h = resp.headers.get("Retry-After")
    if not h:
        return None
    if h.strip().isdigit():
        return int(h)
    try:
        when = parsedate_to_datetime(h)
        return max(0.0, (when - datetime.now(timezone.utc)).total_seconds())
    except (TypeError, ValueError):
        return None

def get_with_backoff(url, tries=5, base=0.5, cap=30.0, **kw):
    for attempt in range(tries):
        resp = requests.get(url, timeout=10, **kw)
        if resp.status_code not in (429, 503) or attempt == tries - 1:
            return resp
        hinted = retry_after_seconds(resp)
        delay = hinted if hinted is not None else random.uniform(0, min(cap, base * 2 ** attempt))
        time.sleep(delay)
```

>

Only retry requests that are safe to repeat. A `GET` is fine; a `POST` that charges a card is not, unless the API supports an idempotency key. Rate-limited requests were usually not processed, but check the API's documentation rather than assuming.

## Wrong vs right

Wrong: ~~`while (res.status === 429) res = await fetch(url)`~~

Right: Honor `Retry-After`; otherwise back off exponentially with jitter, with a retry cap

Why: Immediate retries extend the overload you just caused, and on many APIs they count against your quota too.

Wrong: ~~Server rate limit returns `503 Service Unavailable`~~

Right: Per-client limits return `429`; reserve `503` for whole-service overload or maintenance

Why: The [nginx `limit_req` module](https://nginx.org/en/docs/http/ngx_http_limit_req_module.html) defaults to `limit_req_status 503;`. Monitoring then pages someone because the service looks down, when really one client was too eager.

```
http {
    limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
    server {
        location /api/ {
            limit_req zone=api burst=20 nodelay;
            limit_req_status 429;
        }
    }
}
```

Wrong: ~~Using `403` to throttle search engine crawlers~~

Right: Use `429` or `503` briefly, or reduce load another way

Why: Google's docs say: "Don't use 401 and 403 status codes for limiting the crawl rate." Google treats 429 as [a signal that the server is overloaded](https://developers.google.com/search/docs/crawling-indexing/http-network-errors) and slows down; but persistent server errors can eventually drop URLs from the index, so this is a short-term measure.

## Common causes

| Cause | What it looks like | Fix |
|---|---|---|
| Loop or retry storm in your code | Hundreds of identical requests per second | Add backoff; cache results; fix the loop |
| Parallelism too high | Batch job runs fine with 2 workers, 429s with 50 | Limit concurrency; use a token bucket on the client |
| Shared IP | 429 on the first request from an office, VPN or CI runner | Authenticate so limits apply per key, not per IP |
| Wrong tier or plan | Limits far lower than expected | Check the API's published limits for your plan |
| Login throttling | 429 after several failed passwords | Wait; this limit is deliberate and protects your account |
| Polling instead of webhooks | Steady background traffic eating the quota | Switch to webhooks or conditional requests (ETags) |

### If you are a visitor

- Stop refreshing; each refresh can reset or extend the wait.
- Wait a minute or two, then try once.
- If you are on a shared network (VPN, office, mobile carrier), try another connection: you may be sharing a limit with many other people.
- Close extensions or scripts that might be polling the site in the background.

## Designing a limit people can live with

The [Handling Overload](https://sre.google/sre-book/handling-overload/) chapter of Google's SRE book argues for per-customer limits and for clients that throttle themselves before the server has to. In practice: publish your limits, send `Retry-After` on every 429, return the quota headers on successful responses too so clients can slow down before they hit the wall, and explain the limit in the body in plain language. A 429 that tells callers exactly what happened and when to come back is a 429 they will only see once.

## Frequently asked questions

**What does HTTP 429 mean?**

RFC 6585 defines 429 Too Many Requests as the user having sent too many requests in a given amount of time, which is rate limiting. The response may include a Retry-After header saying how long to wait.

**How long should I wait after a 429?**

If the response has a Retry-After header, wait at least that long; it can be a number of seconds or an HTTP date. If it does not, back off exponentially with random jitter, starting small and capping the maximum delay.

**Is 429 a client error or a server problem?**

It is a 4xx code, so formally the client is expected to change its behaviour (slow down). Google's crawlers, however, treat 429 like a server error, as a signal that the server is overloaded, and slow their crawling.

**Should rate limits return 429 or 503?**

429 when a specific caller has exceeded its allowance; 503 when the whole service is overloaded or in maintenance. nginx's limit_req returns 503 by default, which you can change with limit_req_status 429.

**What are the RateLimit and RateLimit-Policy headers?**

They come from an IETF HTTP API working group Internet-Draft (version 11, May 2026) that standardises how servers advertise quotas: RateLimit-Policy describes the policy, RateLimit reports the remaining quota and time window. It is not yet an RFC.

## Sources

1. [RFC 6585 §4: 429 Too Many Requests](https://www.rfc-editor.org/rfc/rfc6585#section-4)
2. [RFC 6585 §7.2: Security considerations for 429](https://www.rfc-editor.org/rfc/rfc6585#section-7.2)
3. [RFC 9110 §10.2.3: Retry-After](https://www.rfc-editor.org/rfc/rfc9110#name-retry-after)
4. [IETF draft-ietf-httpapi-ratelimit-headers](https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/)
5. [Marc Brooker, Exponential Backoff And Jitter (AWS Architecture Blog)](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/)
6. [nginx: ngx_http_limit_req_module](https://nginx.org/en/docs/http/ngx_http_limit_req_module.html)
7. [Google Search Central: How HTTP status codes affect Google Search](https://developers.google.com/search/docs/crawling-indexing/http-network-errors)
8. [GitHub Docs: Troubleshooting the REST API (rate limits)](https://docs.github.com/en/rest/using-the-rest-api/troubleshooting-the-rest-api)
9. [MDN: 429 Too Many Requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/429)
10. [Google SRE Book: Handling Overload](https://sre.google/sre-book/handling-overload/)

---
Source page: https://erroneo.us/developers/http-429-too-many-requests/ · More: https://erroneo.us/llms.txt
