HTTP 429 Too Many Requests: Retry-After and a safe retry plan
HTTP 429 means the server is rate limiting a client. Read its response and Retry-After header, reduce the shared request rate, and retry only operations that are safe to repeat. A retry loop needs a deadline and an attempt limit.
In this guide
- First identify which component returned 429
- Read Retry-After in the correct units
- Budget the combined queue, not each worker separately
- Use bounded backoff with jitter when no delay is supplied
- Retry only when repeating the operation is safe
- Verify the fix with a small controlled batch
- Common questions
- Sources & definitions
A website integration can work during a manual check and fail when a batch starts. Ten workers may each look modest while their combined traffic exceeds one account limit. Treat the 429 response as feedback about the whole workload, rather than a reason for every worker to immediately send the same request again.
This guide gives you a diagnostic record, an example queue budget and a delay calculation you can adapt. The numbers describe an imaginary service with a documented limit; they are not limits for a particular API. Always read the contract of the service returning the error.
First identify which component returned 429
Record the request time, endpoint, method, status, request ID and retry-related response headers. Keep authorization headers and personal data out of the log. Check whether the response came from the intended API, your reverse proxy or a CDN rule. A branded error page alone does not identify the limiting policy.
MDN documents that limits can apply to an IP address, an authenticated user, an application or a resource. The response body or provider dashboard may explain which quota was reached. A per-minute rate, a concurrent-request limit and a daily allowance need different fixes: slowing a queue cannot replenish a daily allowance that is exhausted.
- Did traffic rise because more jobs ran, or because one job started retrying?
- Do several services, browser tabs or API keys share the same account allowance?
- Is the limit about request count, active requests, tokens or another resource?
- Does the documented recovery time fit the deadline of this user action?
Read Retry-After in the correct units
Retry-After can be a non-negative integer number of seconds or an HTTP date. For example, a value of 12 requests a twelve-second wait after receipt, not twelve milliseconds. If it contains a date, calculate the remaining interval using a reliable clock; an already elapsed date does not justify a tight retry loop.
For a queue that can wait, use the valid server delay as a minimum and add a small random spread when many workers resume together. If the requested delay exceeds the remaining job deadline, stop or reschedule the job. Do not silently shorten the server delay to an arbitrary local maximum and then keep calling too early.
| Response observation | Interpretation | Action for this job |
|---|---|---|
| Retry-After: 12 | Delay is 12 seconds | Wait at least 12 seconds if the deadline allows |
| Retry-After: an HTTP date | Delay depends on receipt time and clock accuracy | Compute a non-negative interval; check the job deadline |
| Header missing or invalid | No usable delay was supplied | Apply the provider policy or a bounded local backoff |
| Provider says daily quota exhausted | Short retries will not restore the allowance | Stop and resume only when quota is available |
Budget the combined queue, not each worker separately
Suppose a service permits 120 requests per minute for the entire account. Your job has 600 requests. With no other activity, the count alone implies at least five minutes of allowance. Completing all 600 in ten seconds and then sleeping fifty seconds is still a burst; the service may reject it even though the minute average appears acceptable.
For this example, choose an operating target of 90 requests per minute to leave room for other work. That is 1.5 requests per second, so the nominal spacing is about 667 milliseconds. Six hundred requests then need about 400 seconds before response time and retries. These are planning calculations, not a promise that this spacing matches every provider window.
If requests take two seconds on average, Little’s Law gives an average in-flight count near 1.5 × 2 = 3 under a stable workload. A semaphore can bound active requests, while a shared rate limiter spaces starts. One control does not replace the other. Count retry attempts against the same budget as initial attempts.
Example workload assumptions:
Account allowance: 120 requests/minute
Operating target: 90 requests/minute = 1.5 requests/second
600 requests / 1.5 requests/second = 400 seconds
Average concurrency at 2 seconds/response: 1.5 × 2 = 3Use bounded backoff with jitter when no delay is supplied
A useful fallback policy increases the waiting window after repeated failures and randomizes the actual wait. For example, choose windows of one, two, four and eight seconds, with a separate total deadline. Jitter spreads workers through each window so that they do not all retry at exactly the same instant.
The function below only calculates a delay; it does not send requests or implement a complete retry client. The caller must enforce the deadline, an attempt cap, cancellation and the safety of the operation. A browser may also need the server to expose Retry-After through CORS before JavaScript can read that header.
function retryDelayMs(retryAfter, attempt, nowMs = Date.now()) {
const text = String(retryAfter ?? "").trim();
if (/^\d+$/.test(text)) {
const milliseconds = Number(text) * 1000;
return Number.isSafeInteger(milliseconds) ? milliseconds : Infinity;
}
if (/^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2} (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d{2}:\d{2}:\d{2} GMT$/.test(text)) {
const dateMs = Date.parse(text);
if (Number.isFinite(dateMs)) return Math.max(0, dateMs - nowMs);
}
const windowMs = Math.min(8000, 1000 * 2 ** attempt);
return Math.floor(Math.random() * windowMs);
}
// Example policy: at most 4 retries and a 30-second total deadline.
// If the required wait exceeds the remaining deadline, stop.Retry only when repeating the operation is safe
Reading a resource and creating a payment are different operations. Before retrying a write, check the service’s idempotency contract and whether the earlier attempt could already have taken effect. Reuse the documented idempotency key for the same logical operation when supported; generating a fresh key for every attempt can create separate operations.
An SDK may already retry internally. Wrapping it with another four-attempt loop can multiply traffic. Write down the maximum total number of network attempts across all layers. For a user-facing action, display a useful retry-later state instead of hiding a minute-long queue behind a permanently spinning button.
- Set one total job deadline and propagate cancellation to queued work.
- Bound both concurrent requests and start rate across the shared allowance.
- Release completed response resources before sleeping or retrying.
- Stop on exhausted quota or a failure that the provider describes as permanent.
- Log attempts and final outcomes so retries are visible in monitoring.
Verify the fix with a small controlled batch
Start with a workload you can count, such as twenty reads, and record how many initial attempts, retries, successful responses and final failures occurred. Confirm that the observed spacing and active-request count match the plan. Then test a simulated 429 with a known Retry-After value without repeatedly stressing the live service.
Use the request-rate calculator to convert your proposed rate between seconds and minutes, and the concurrency calculator to check a latency assumption. Compare the resulting plan with the provider’s actual limits. If 429 responses remain, inspect the shared callers and quota type before increasing delays blindly.
Common questions
Does a 429 response mean my IP address is banned?
Not necessarily. It indicates rate limiting. The policy may apply to an account, token, route, IP address or broader service. Read the response details and provider documentation before choosing a remedy.
Should I retry immediately if Retry-After is missing?
No immediate retry is required by the absence of that header. Use the documented provider policy, or a bounded backoff with jitter, and keep a shared limit on total attempts.
Will adding more workers fix Too Many Requests?
More workers can increase pressure on a shared allowance. First identify the limiting dimension, then coordinate the queue and concurrency. Extra capacity helps only when the provider contract actually permits it.
Sources & definitions
Method checked: