Why you're getting a 429 Too Many Requests error
API & BackendA 429 is the server telling you to slow down, not that your request was malformed. This page explains what it means, how to read the headers that come with it, and what to do next.
A 429 means the server has decided you have sent too many requests in some window and is refusing this one. It is rate limiting, not a fault in the request itself — the same request will usually succeed once you wait. The response may carry a Retry-After header saying when to try again, given either as a number of seconds or as an absolute date. You can also get a 429 while apparently under the published limit, because the specification lets every server decide for itself what it counts and who it counts it against.
What the server is actually counting
Retry-After comes in two forms, and you must handle both
Retry-After = delay-seconds / HTTP-date
Both forms are legal and you will meet both in the wild.
Delay-seconds is a non-negative integer count of seconds to wait:
Retry-After: 120
HTTP-date is an absolute timestamp. The format required for generating it is IMF-fixdate (RFC 9110 §5.6.7), which always ends in GMT:
Retry-After: Sun, 06 Nov 1994 08:49:37 GMT
Two things routinely go wrong here. The first is parsing: code that does parseInt(retryAfter) on a date string gets NaN, and code that treats NaN as zero retries immediately — which on most APIs extends the penalty rather than ending it. Branch on whether the value is all digits before deciding which parser to use.
The second is clock skew. An absolute date is only as good as your machine's clock, and a client running a few minutes fast will retry early every time. The 429 response also carries a Date header holding the server's own view of now; compute the wait as the difference between the Retry-After date and that Date header, not against your local clock. Note also that recipients are expected to accept two obsolete date formats (rfc850-date and asctime-date) even though senders should no longer produce them, so a strict parser can still fail on a compliant-enough server. X-RateLimit-Reset and the headers that are not standard
X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. These are conventions, not a standard. Nothing defines their units, so they differ between providers, and this is where integrations quietly break.
X-RateLimit-Reset is the worst offender because it has at least three readings in common use:
— a Unix timestamp in seconds, meaning "the window resets at this absolute moment"
— a number of seconds remaining until the window resets
— a Unix timestamp in milliseconds
The values look similar enough that the wrong reading survives testing. A ten-digit number is almost certainly a Unix timestamp in seconds; a small number like 59 is almost certainly a duration. Rather than infer, check the provider's documentation once and pin the interpretation in your client — and prefer Retry-After when the response gives you both, since its units are actually specified.
There is an IETF effort to standardise this area, draft-ietf-httpapi-ratelimit-headers, which defines RateLimit and RateLimit-Policy fields to replace the ad-hoc X-RateLimit-* set. It is an active Internet-Draft and has not been published as an RFC, so treat any RateLimit-* headers you receive today as provider-specific until that changes. Why you can get a 429 while apparently under the limit
Retry-After says, and whether a Via, Server, or CDN header names an intermediary will usually identify which of the above you are looking at within one incident. Work out the request budget for a window
Once you know which limit applies to you, the next question is usually arithmetic: how many requests does a published rate actually buy over a given stretch of time? Enter the rate and the window to size a batch job, a sync, or a polling interval against it.
Last updated: August 2026
How to Calculate a Request Budget from a Rate Limit
1. Enter your rate limit in requests per second (RPS). If your API publishes a per-minute or per-hour limit, divide by 60 or 3,600 to convert. 2. Enter the length of the window you care about. 3. Select the unit: seconds, minutes, or hours. 4. The calculator converts the window to seconds and multiplies by your RPS to give the total requests the limit allows across it. 5. Treat the result as a ceiling, not a target — it assumes one limit, evenly paced requests, and no retries.
Formula
Total Requests = Requests per Second (RPS) × Duration in Seconds Duration conversions: - Minutes → multiply by 60 - Hours → multiply by 3,600 Useful conversions: - 100 req/min = 100 ÷ 60 ≈ 1.67 RPS - 1,000 req/hr = 1,000 ÷ 3,600 ≈ 0.28 RPS This is the sustained-rate ceiling only. It does not model burst capacity, concurrency caps, or a second limit applied over a longer period.
Worked Examples
Example 1 — Sizing an hourly sync against a 50 RPS limit
RPS: 50 Window: 1 hour = 3,600 seconds Budget = 50 × 3,600 = 180,000 requests The job needs 200,000 records at one request each: 200,000 ÷ 180,000 = 1.11 hours — it does not fit in the window. Options: spread it over two hours, batch several records per request, or move to a tier with a higher limit.
Example 2 — Reading a Retry-After you were given
Response: HTTP/1.1 429 Too Many Requests Date: Sun, 06 Nov 1994 08:49:07 GMT Retry-After: Sun, 06 Nov 1994 08:49:37 GMT Wait = Retry-After − Date = 30 seconds. Computed against the server Date header, the answer is 30 seconds on any machine. Computed against a local clock running 5 minutes fast, the same response yields a negative wait and the client retries instantly.
Example 3 — The failure case: under budget, still refused
Limit: 100 RPS Window: 60 s Budget = 100 × 60 = 6,000 requests Sent: 4,000 requests in the minute — comfortably under budget. Result: several hundred 429s. The 4,000 were sent as four bursts of 1,000 in under a second each. The sustained rate was 67 RPS; the instantaneous rate was ~1,000 RPS against a bucket whose burst capacity was 200. The arithmetic above was never wrong — it answers a different question than the one the server was asking.
Handling 429s in Client Code
- › Branch on the format of Retry-After before parsing it. If the value is all digits it is a count of seconds; otherwise it is an HTTP date. Treating a date as an integer yields NaN, and a NaN wait usually becomes an immediate retry.
- › Compute absolute waits against the response Date header rather than the local clock, so a skewed client does not retry early on every 429.
- › Add jitter to backoff delays. Without it, every client throttled by the same incident retries at the same instant and reproduces the burst that caused it.
- › Cap the number of retries and surface the failure. Retrying a 429 indefinitely converts a rate-limit problem into an outage that is harder to diagnose.
- › Log the full headers of the 429 response, including Via and Server. Whether the refusal came from the origin or an intermediary changes which limit you need to fix.
- › Throttle on the way out rather than reacting on the way back. A token bucket in your client keeps you under the limit without needing the server to refuse you first.