HTTP 429: reading rate limit headers and backing off properly
A 429 response means you sent more requests than the server is willing to accept from you right now. Unlike most 4xx codes it is not telling you the request was malformed. It is telling you the request was fine and the timing was not, and it usually carries instructions about when to try again.
Clients that treat it as a generic error and retry immediately turn a brief throttle into a sustained outage, for themselves and sometimes for the service.
Read Retry-After first
Retry-After is defined in RFC 9110 and takes one of two forms.
Delay in seconds:
Retry-After: 30
Or an absolute HTTP date:
Retry-After: Wed, 18 Aug 2026 12:00:00 GMT
A correct client parses both. The date form is less common but appears often enough that assuming an integer produces a parse failure at the worst moment.
When Retry-After is present, it is authoritative. Waiting less than it says will earn another 429, and on services that penalise repeat offenders it can extend the window. Waiting the stated time is both the fastest route to success and the polite one.
The same header appears on 503 Service Unavailable, where it signals planned or temporary unavailability rather than a quota. The handling is the same.
The rate limit headers
Beyond Retry-After, most APIs expose the state of your quota. There is now a standards track draft, and headers of this form are increasingly common:
RateLimit-Limit: 100
RateLimit-Remaining: 0
RateLimit-Reset: 42
Plenty of services still use the older X- prefixed variants, and they do not agree on the units:
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1755518400
The trap is Reset. In the newer form it is usually seconds remaining. In the GitHub style form it is a Unix timestamp. Treating a timestamp as a delay makes your client wait about fifty five years, and treating a delay as a timestamp makes it retry instantly. Check which one a given API means rather than inferring it from the name.
Reading these on a live endpoint is the quickest way to find out. The API tester shows the full response headers for a request, which is where the actual names and values become obvious. HTTP headers explained covers the wider set.
Use the quota before you hit the wall
The useful move is to watch Remaining on every response and slow down before it reaches zero, rather than sprinting into a 429 and recovering. A client that notices it has ten requests left in the window and spreads them over the remaining time never gets throttled at all.
This matters most for batch work. A job iterating over ten thousand records will exhaust any quota if it runs flat out, and pacing it against the headers turns an unreliable job into a predictable one.
Backing off correctly
When no Retry-After is given, back off exponentially. Double the wait after each failure, from a small base, up to a ceiling:
1s, 2s, 4s, 8s, 16s, 32s, 60s, 60s ...
Then add jitter, which is the part most implementations omit.
Without jitter, every client that failed at the same moment retries at the same moment. If a service throttles a thousand clients at once, all thousand wait exactly one second and return together, then two seconds and return together. The retries arrive in synchronised waves that keep the service at the edge of failure. This is the thundering herd, and it is caused by the backoff rather than relieved by it.
Full jitter picks a random value between zero and the current ceiling:
delay = random(0, min(cap, base * 2 ** attempt))
The waves flatten into a spread, and the service drains its backlog. The randomness is doing the real work here, not the exponent.
Three more rules keep it sane:
- Cap the total. Bound either the number of attempts or the overall elapsed time, so a failing dependency cannot hold a request open indefinitely.
- Only retry what is safe to repeat. GET, HEAD and PUT are idempotent by definition. POST is not, so retrying one risks a duplicate. If the API supports an idempotency key, send one and retries become safe.
- Do not retry every status. A 429 and a 503 are worth retrying. A 400 or a 422 will fail identically no matter how long you wait, and retrying them wastes quota that the retryable calls need.
Why you might be throttled without exceeding your own limit
If a 429 arrives well below the documented allowance, one of these is usually why:
- The limit is shared. Quota is often applied per account or per organisation, not per client. Another service on the same credentials is consuming it.
- It is per endpoint. Expensive routes such as search frequently carry their own tighter limit than the general one.
- Concurrency is limited separately from rate. Some services cap simultaneous in flight requests as well as requests per minute. Ten parallel workers can trip a concurrency cap while staying under the rate.
- You are behind a shared address. Where the limit is per IP, a NAT gateway or CI runner pools many clients into one bucket. Checking what address you actually present with the IP lookup sometimes explains an otherwise impossible throttle.
- A burst allowance was consumed. Token bucket schemes permit a short burst then settle to a slower sustained rate, so a fast start followed by throttling is the design working as intended.
Choosing a status when you implement limiting
If you are on the other side of this, a few conventions make life easier for the clients you serve.
Return 429, not 403. A 403 says the request is not permitted, which sends the client's author looking at credentials rather than at timing.
Always send Retry-After. It is the single most useful thing in the response, and it lets a well behaved client recover without guessing.
Expose the quota headers on successful responses too, not only on the rejection. Clients can then pace themselves, which reduces the number of 429s you have to serve in the first place.
Document which units Reset uses. Every ambiguity here becomes a bug in someone's client.
For the neighbouring status codes and where 429 sits among them, see HTTP status codes explained, and the status code reference has the full list with usage notes.