Too Many Requests
ActiveHTTP 429 Too Many Requests indicates the user has sent too many requests in a given time period. Defined in RFC 6585 §4. The server SHOULD include a Retry-After header indicating how long to wait before making a new request.
Description
The 429 Too Many Requests status code indicates that the user has sent too many requests in a given amount of time. The response SHOULD include details explaining the condition, and MAY include a Retry-After header indicating how long to wait before retrying.
Retry-After can take two forms: Retry-After: 60 (delta-seconds – wait 60 seconds from now) Retry-After: Fri, 31 Aug 2026 12:00:00 GMT (HTTP-date – wait until this absolute time)
Delta-seconds is simpler for clients, but HTTP-date is useful when the rate limit window resets at a fixed wall-clock boundary (e.g., top of the hour). Clients must support both forms per RFC 9110 §10.2.4.
The spec does not define how the server identifies the user or counts requests. Rate limiting can be per-user, per-IP, per-API-key, per-endpoint, or global – the approach is entirely up to the implementation.
429 applies only to rate limiting. For capacity-based refusals where the server is overloaded, 503 Service Unavailable is more appropriate. The difference: 429 means 'you specifically are sending too much', 503 means 'we can't handle anyone right now'.
Examples
GET /api/data HTTP/1.1
Host: api.example.com
Authorization: Bearer token123HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1756684800
{"error": "rate_limit_exceeded", "message": "Rate limit of 100 requests per minute exceeded. Retry after 60 seconds."}HTTP/1.1 429 Too Many Requests
Retry-After: Sun, 31 Aug 2026 14:00:00 GMT
Content-Type: application/json
RateLimit-Limit: 1000
RateLimit-Remaining: 0
RateLimit-Reset: Sun, 31 Aug 2026 14:00:00 GMT
RateLimit-Policy: 1000;w=3600
{"error": "rate_limit_exceeded", "message": "Hourly quota exhausted. Window resets at 14:00 UTC."}HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1756684800
{"data": [...]}Edge Cases
- •Retry-After can be delta-seconds (integer) or an HTTP-date. Clients MUST support both forms (RFC 9110 §10.2.4). Delta-seconds: 'Retry-After: 60'. HTTP-date: 'Retry-After: Sun, 31 Aug 2026 14:00:00 GMT'.
- •Rate-limit header names are not standardized. Common variants: X-RateLimit-Limit / X-RateLimit-Remaining / X-RateLimit-Reset (GitHub, Stripe), RateLimit-Limit / RateLimit-Remaining / RateLimit-Reset (IETF draft draft-ietf-httpapi-ratelimit-headers). Always document which headers your API uses.
- •429 vs 503: 429 means this specific client is over limit. 503 means the server is overloaded for all clients. Clients that retry immediately on 429 amplify the load that caused the rejection. Always use the Retry-After value.
- •Gateway-generated vs origin 429: check if CF-Ray (Cloudflare) or X-Amzn-RequestId (AWS) is in the response. If yes, the rate limit fired before your code ran – the fix is in your CDN or gateway config, not your application.
- •Always send rate-limit headers on every response (200, 201, etc.) – not only on 429. Clients that can see they are approaching the limit will back off before you have to reject them.
- •Idempotency with POST: a 429 during a payment or order creation may mean the operation started. Always implement idempotency keys so clients can safely retry without creating duplicates.
- •Exponential backoff with jitter: naive clients retry immediately on 429, turning one overload into a sustained thundering herd. Recommended: wait = min(cap, base * 2^attempt) + random(0, base). The jitter spreads retries across time.
- •Responses with status 429 MUST NOT be stored by a shared cache – the rate limit applies to the specific client, not all clients (RFC 6585 §4).
When You'll See This
- →API rate limit exceeded
- →Too many login attempts (brute-force protection)
- →Web scraping detection
- →DDoS mitigation by reverse proxies
Implementation References
| Language | Constant |
|---|---|
| Go | http.StatusTooManyRequests |
| Rust | http::StatusCode::TOO_MANY_REQUESTS |
| Python | http.HTTPStatus.TOO_MANY_REQUESTS |
| Node.js | http.STATUS_CODES[429] |
| .NET | HttpStatusCode.TooManyRequests |
| Java | (no built-in constant, use 429 literal) |
History
Introduced in RFC 6585 (April 2012) as part of 'Additional HTTP Status Codes'. Created because 403 was being overloaded to mean rate-limited, which confused the semantics.
Related Status Codes
Related Headers
FAQ
What does HTTP 429 Too Many Requests mean?
HTTP 429 means you have sent too many requests in a given time window (rate limiting). The server is telling you to slow down. Wait for the duration specified in the Retry-After header before retrying. If no Retry-After is present, implement exponential backoff starting with at least 1 second.
What is the difference between token bucket, leaky bucket, fixed window, and sliding window rate limiting?
Fixed window: count requests per discrete time window (e.g., 0:00–0:59, 1:00–1:59). Simple but allows burst at window boundaries – a client can send the full quota at 0:59 and again at 1:00. Sliding window: count requests in a rolling window ending at 'now'. Smoother, prevents boundary burst, higher memory overhead per client. Token bucket: each client accumulates tokens at a fixed rate (e.g., 1 token/second, max 60). Each request consumes 1 token. Allows burst up to bucket capacity. Most API rate limiters use this. Leaky bucket: requests queue up; a worker drains them at a fixed rate. Provides perfectly smooth output but increases latency. The server-side algorithm determines the exact semantics of your Retry-After timing.
What rate-limit response headers should my API send?
There is no HTTP standard for rate-limit headers yet, but IETF draft-ietf-httpapi-ratelimit-headers defines: RateLimit-Limit (quota per window), RateLimit-Remaining (requests left), RateLimit-Reset (when window resets, as HTTP-date or delta-seconds), RateLimit-Policy (machine-readable quota policy). GitHub uses X-RateLimit-Limit / X-RateLimit-Remaining / X-RateLimit-Reset (Unix timestamp). Stripe uses X-RateLimit-Limit-Requests and X-RateLimit-Limit-Burst. Always document which format your API uses and send these headers on every response, not just 429.
How should I implement retry logic after a 429?
First, check Retry-After. If present, wait exactly that duration before retrying. If absent, use exponential backoff with jitter: wait = min(maxWait, baseWait * 2^attempt) + random(0, baseWait). A reasonable default: base 1s, max 60s. Add jitter to prevent thundering herd when many clients hit the limit simultaneously. For non-idempotent operations (POST, PATCH), use idempotency keys to safely retry without creating duplicates.