HTTP 429: How Long to Wait, When Retry-After Is Missing, and Which Limit Is Actually Binding (2026)
Requests started coming back 429. You need to keep working without making the situation worse or losing the requests you already have queued.
Honour Retry-After if the response carries one, and otherwise back off exponentially with jitter. A 429 means the request was understood and refused for pacing, so nothing was processed and the identical request is safe to retry once the wait has elapsed. Retrying immediately is the one response that reliably makes things worse.
Read the response before you decide
A 429 with Retry-After is the server telling you exactly when it will accept work again, and honouring it beats any calculation you would make locally. Many APIs also return headers naming which limit was hit and when the window resets — that is the difference between waiting the right amount and guessing.
Why jitter is not optional
Without jitter, every client that failed at the same moment retries at the same moment. The backoff spreads attempts out in time for one client but synchronises them across all of them, reproducing the spike that caused the limit. Full jitter — a random wait between zero and the computed delay — costs nothing and removes the effect.
Bound by time, not by attempts
Limit retries by total elapsed time rather than by count. A user-facing request that has been retrying for ten seconds has already failed from the user perspective, however many attempts remain in the budget. Background work can afford a longer ceiling, which is why the two should not share one policy.
Which failures deserve a retry
Retry timeouts, 429 and 5xx, because all three can succeed unchanged later. Do not retry 400 or 422: the server understood the request and rejected its content, so an identical retry fails identically and only adds load.
Read the response before you decide anything
A 429 carrying Retry-After is the server stating when it will accept work again, and honouring it beats any calculation made locally. Many APIs also return headers naming which limit was hit, how much of it remains, and when the window resets. Those three values turn a guess into arithmetic: if the reset is in eighteen seconds and you have zero remaining, the correct wait is eighteen seconds, not whatever your backoff curve happens to produce. Reading them costs nothing and removes an entire category of tuning.
When Retry-After is absent, and what to do then
The header is optional and plenty of APIs omit it. Its absence is not an invitation to retry immediately; it means the server has declined to tell you when to return, so you fall back to exponential backoff with jitter. Absence is also common on 429s produced by an intermediary — a CDN or a WAF refusing before the request reached the application — which is worth noticing, because those limits are usually per-IP rather than per-key and adding more machines makes them worse rather than better.
The two formats Retry-After can take
Retry-After is either a number of seconds or an HTTP date, and a parser that assumes one will silently mishandle the other. A date parsed as an integer yields zero, which turns a polite instruction to wait into an immediate retry — the exact behaviour the header exists to prevent. Parse both forms, and clamp the result: a server occasionally returns a wait measured in hours, and a client that sleeps for three hours inside a request handler has stopped being a client.
Why jitter is not optional
Without jitter, every client that failed at the same moment retries at the same moment. Backoff spreads one client attempts out in time but leaves all clients synchronised, so the retry wave reproduces the spike that caused the limit. Full jitter — a uniform random wait between zero and the computed delay — costs one line and removes the correlation entirely. Jitter the ceiling too: clients that reach an unjittered maximum converge on the same fixed interval from then on, recreating the synchronisation the earlier randomness had just broken up.
Bound the retry budget by time, not by attempt count
Limit retries by total elapsed time rather than by number of attempts. A user-facing request that has been retrying for ten seconds has already failed from the user perspective, however many attempts remain in the budget. Background work can afford a much longer ceiling, which is precisely why the two should not share one policy. A single retry configuration applied to both produces either an interface that hangs or a job that gives up too early.
Which failures deserve a retry at all
Retry timeouts, 429 and 5xx, because all three can succeed unchanged later. Do not retry 400 or 422: the server understood the request and rejected its content, so an identical retry fails identically and adds load to a service that is already telling you something. A 401 is worth exactly one retry after refreshing credentials and none after that. A 403 should never be retried, because permission does not change on the timescale of a backoff curve.
The boundary case: which limit is actually binding
A client well under the documented per-minute limit can still be rate-limited, and this is the most commonly misdiagnosed 429 there is. Limits are enforced in parallel, not as a single number: requests per minute, tokens per minute, concurrent requests in flight, and a burst allowance measured over a much shorter window than the one you are watching. Sixty requests per minute with three concurrent slots is not sixty fast requests; it is three at a time, and if each takes two seconds the real ceiling is ninety per minute only in theory and thirty in practice. When a 429 arrives while your own counter says you are at half the limit, the binding constraint is a different one.
Per-key against per-IP, and why scaling out can make it worse
Most API limits are enforced per credential, so distributing traffic across more machines does not raise the ceiling — it merely spreads the same allowance thinner and makes the timing harder to reason about. Limits imposed by an edge layer are more often per-IP, which inverts the advice: there, adding source addresses does help, and consolidating traffic behind one NAT gateway is what causes the problem. Knowing which of the two you face determines whether horizontal scaling is a fix or an aggravation.
Client-side pacing beats server-side rejection
A limiter on your side that refuses to exceed the known ceiling converts rejected requests into queued ones, which is strictly better: the work is delayed rather than discarded, and the server is not spending capacity refusing you. A token bucket sized to the documented limit, shared across workers through a central store, keeps aggregate traffic under the cap even as instance count changes. The alternative — discovering the limit by hitting it — wastes a round trip on every rejection and adds load exactly when the service is least able to absorb it.
What a queue in front of the limiter buys you
Once retries are bounded and paced, the remaining question is what happens to work that cannot be sent yet. Holding it in a queue makes the backlog visible and the ordering deliberate; holding it in retry loops spread across request handlers makes it invisible and its ordering accidental. A queue also gives you somewhere to apply priority, so a user-facing call is not stuck behind a bulk import that could have waited an hour.
Monitoring: track 429 separately from errors
Folding 429 into a general error rate hides the only signal that tells you what to change. A steady trickle is backpressure working as designed and needs no action. A step change means either your traffic shape moved or the provider limit did, and those have different fixes. Track the rate, the endpoints producing it, and the distribution of Retry-After values: a wait that keeps growing is a service shedding load, and a client that keeps retrying into it is part of the problem.
Idempotency is what makes aggressive retrying safe
A 429 guarantees the request was not processed, so retrying it unchanged is safe. Nothing else in this list carries that guarantee. A 500 or a timeout may have applied part of the work, which means a retry without an idempotency key can duplicate an effect. Retry policy and idempotency are therefore one design decision, not two: how hard you are willing to retry is bounded by how safe repetition is.
Testing the path you hope never runs
Retry logic is the code most likely to be wrong and least likely to be exercised, because it only runs when something else has already failed. Test it directly: assert that a 429 with Retry-After waits that long, that an HTTP-date value is parsed rather than treated as zero, that a 422 is not retried at all, and that the total elapsed budget is respected. A retry loop nobody has tested is a retry loop that runs once in production, on the worst day, unobserved.
Total wait by attempt, 200 ms base with full jitter
Total wait by attempt, 200 ms base with full jitter
| Attempt | Computed delay | Average wait | Worst case |
| 1 | 200 ms | 100 ms | 200 ms |
| 2 | 400 ms | 300 ms | 600 ms |
| 3 | 800 ms | 700 ms | 1.4 s |
| 4 | 1.6 s | 1.5 s | 3.0 s |
| 5 | 3.2 s | 3.1 s | 6.2 s |
| 6 | 6.4 s | 6.3 s | 12.6 s |
Which status codes to retry, and how
Which status codes to retry, and how
| Status | Retry | Bare retry safe | Notes |
| 408 Request Timeout | yes | no | Server gave up waiting; work may have started |
| 429 Too Many Requests | yes | yes | Guaranteed unprocessed |
| 500 Internal Server Error | yes | no | May be partly applied; needs a key |
| 502 / 504 Gateway | yes | no | Upstream may have received it |
| 503 Service Unavailable | yes | no | Honour Retry-After if present |
| 400 / 422 | no | n/a | Request content is the problem |
| 401 Unauthorized | once | yes | Only after refreshing credentials |
| 403 Forbidden | no | n/a | Permission does not change on a backoff timescale |
Retry-After: the two formats and how each fails
Retry-After: the two formats and how each fails
| Value | Meaning | Failure if mis-parsed |
| 120 | Wait 120 seconds | None; the common case |
| Wed, 21 Oct 2026 07:28:00 GMT | Wait until that instant | Parsed as int gives 0, retries immediately |
| 0 | Retry now | Honour it, but still apply jitter |
| -1 or garbage | Malformed | Fall back to backoff, do not treat as 0 |
| 86400 | Wait a day | Clamp; no request handler should sleep that long |
Which limit is binding when you are under the headline number
Which limit is binding when you are under the headline number
| Limit type | Typical unit | Symptom when it binds |
| Requests per minute | req/min per key | 429 at a predictable count |
| Tokens per minute | tokens/min | 429 on large payloads, not on small ones |
| Concurrency | requests in flight | 429 under load, never when serial |
| Burst allowance | req per few seconds | 429 in bursts while the minute average is low |
| Per-IP edge limit | req/min per address | Whole fleet limited together behind one NAT |
| Daily quota | req/day | 429 that lasts until midnight UTC |
Retry budgets by workload
Retry budgets by workload
| Workload | Total budget | Max attempts | Reasoning |
| Interactive request | 2-3 s | 2-3 | Beyond this the user has already left |
| Background job | 5-15 min | 6-8 | Nobody is waiting; completion matters more |
| Webhook delivery | hours, spaced | 5-10 | Receiver may be down for maintenance |
| Bulk import | hours | unbounded with pacing | Throughput, not latency, is the goal |
| Health check | none | 0 | A retry hides the failure it exists to report |
Client-side pacing against discovering the limit by hitting it
Client-side pacing against discovering the limit by hitting it
| Pace before sending | Retry after rejection |
| Wasted round trips | none | one per rejection |
| Load added when service is struggling | none | yes |
| Work ordering | deliberate, queued | accidental |
| Needs shared state across workers | yes | no |
| Behaviour when the limit is unknown | must be configured | discovers it |
Key facts
- HTTP 429 guarantees the request was not processed, which is what makes retrying the identical request safe without an idempotency key.
- With a 200 ms base and full jitter, five attempts average about 3.1 seconds of total waiting and 6.2 seconds in the worst case.
- Retry-After may be a number of seconds or an HTTP date, and parsing a date as an integer yields zero, turning a wait instruction into an immediate retry.
- A Retry-After header overrides any client-side backoff calculation, because it is the server stating when it will be ready.
- Backoff without jitter leaves clients synchronised, so the retry wave reproduces the spike that caused the limit.
- Jitter must be applied to the ceiling as well as the delay, or every client that reaches the maximum converges on the same fixed interval.
- Retrying a 400 or 422 is always wasted: the server understood the request and rejected its content, so an identical retry fails identically.
- A 403 should never be retried, because permission does not change on the timescale of a backoff curve.
- Being under the documented per-minute limit does not prevent a 429, because concurrency, token and burst limits are enforced in parallel.
- Most API limits are per credential, so adding machines spreads the same allowance thinner rather than raising the ceiling.
- Edge and WAF limits are more often per-IP, which is why traffic consolidated behind one NAT gateway can be limited as a single client.
- Retry budgets should be bounded by total elapsed time rather than attempt count, because a user-facing request has already failed after a few seconds.
- A daily quota produces a 429 that persists until the quota window resets, which no backoff schedule will outlast.
- A health check should never retry, because the retry hides the failure the check exists to report.
Frequently asked questions
I am under the documented limit but still getting 429s. Why?
A second limit is binding. Concurrency caps, token-per-minute limits and short burst windows are enforced independently of the headline per-minute number, and a client comfortably under one can be well over another. If the response names which limit was hit, that is the answer; if not, test by serialising your requests — if the 429s stop, it was concurrency.
What should I do when there is no Retry-After header?
Fall back to exponential backoff with full jitter. The absence means the server has declined to say when to return, not that you may return immediately. It also hints that the rejection came from an intermediary rather than the application, which usually means the limit is per-IP.
Is it safe to retry a 500 the same way as a 429?
Back off the same way, but not with the same confidence. A 429 guarantees nothing was processed; a 500 may have applied part of the work, so the retry needs an idempotency key to avoid duplicating an effect.
How many retries should I allow?
Bound the total elapsed time rather than the count. Two to three seconds for anything a person is waiting on, minutes for background work. The attempt count then falls out of the budget and the backoff curve rather than being chosen separately.
Should a 429 page an on-call engineer?
Not by itself. A steady rate is backpressure working as intended. A step change is worth an alert, because it means either your traffic shape or the provider limit moved, and those have different fixes.
Does adding more servers help with rate limits?
Usually not, and sometimes it hurts. Most API limits are per credential, so more machines share the same allowance. Edge limits are more often per-IP, where more source addresses do help — so the answer depends entirely on which layer is refusing you.
What is full jitter and how is it different from equal jitter?
Full jitter waits a uniform random time between zero and the computed delay, halving the average wait and spreading retries completely. Equal jitter waits half the delay plus a random half, which spreads slightly less but guarantees a minimum gap. Full jitter recovers faster in aggregate; equal jitter avoids near-zero waits that are certain to fail again.
Can I just catch 429 and sleep for a fixed number of seconds?
It works until more than one client does it. A fixed sleep synchronises every client that failed together, so they all return at the same instant and reproduce the spike. The randomness is the part doing the work, not the waiting.
How do I handle a daily quota that has run out?
Stop retrying and surface it. No backoff schedule outlasts a quota window, so a client that keeps trying burns capacity for hours and hides the real problem, which is that the quota is too small or the work too large.
Should retry logic live in the HTTP client or in the caller?
In the client, so every call site inherits it, with the budget passed in by the caller. Retry policy embedded at each call site drifts, and the sites most likely to need it are the ones least likely to have it.
What does a growing Retry-After value mean?
The service is shedding load progressively, and each retry into it is making that worse. Treat a rising sequence as a signal to stop and alert rather than as a longer wait to honour.
Do I need idempotency keys if I only retry 429s?
Strictly no, since 429 guarantees the request was unprocessed. In practice retry logic rarely stays that narrow — a timeout or a 502 gets added later — and at that moment the guarantee is gone. Adding the key when you add the retry is cheaper than remembering to add it afterwards.
How should client-side pacing work across multiple workers?
Through a shared token bucket, so the aggregate stays under the cap regardless of how many instances are running. A per-instance limiter divides the ceiling by a number that changes whenever the fleet scales, which means it is either wrong or wasteful.
How do I test retry behaviour?
Drive the client against a stub that returns 429 with a known Retry-After, an HTTP-date variant, a 422, and a sequence that eventually succeeds. Assert the waits and the total elapsed budget. Retry code runs only when something else has already broken, so a test is the only place it will be exercised under observation.
Machine-readable copy of this page:
/guide/recover-from-rate-limits.md