429
Too Many Requests
4xx Client Error
What Does HTTP 429 Too Many Requests Mean?
HTTP 429 indicates that the user has sent too many requests in a given amount of time ("rate limiting"). The server is protecting itself from being overwhelmed and is asking the client to slow down.
The response should include a Retry-After header indicating how many seconds the client should wait before making a new request. Many APIs also include custom headers showing rate limit details:
X-RateLimit-Limit— The maximum number of requests allowed per windowX-RateLimit-Remaining— How many requests you have left in the current windowX-RateLimit-Reset— Unix timestamp when the rate limit window resets
Rate limiting is not punishment — it is a necessary mechanism to ensure fair access for all API users and protect the server from abuse or accidental overload.
Common Causes
- API rate limit exceeded: Most APIs enforce per-user rate limits. GitHub allows 5,000 requests/hour for authenticated users. Twitter (X) has tier-based limits. Exceeding these triggers 429.
- Aggressive scraping or crawling: Sending hundreds of requests per second to a website will trigger its rate limiter, even if no formal API rate limit is documented.
- Missing request throttling in client code: A loop that fires API calls as fast as possible without delays. Especially common in data migration scripts and batch processing jobs.
- Shared API keys or IP addresses: Multiple applications or users sharing the same API key or IP (e.g., behind a corporate proxy) collectively hit the limit even though individual usage seems moderate.
- Retry storms: A bug causes failed requests to retry immediately without backoff, creating a cascade of requests that overwhelm the server. This is especially dangerous when multiple clients do it simultaneously.
How to Handle 429 Errors
Exponential Backoff with Jitter (JavaScript)
async function fetchWithRetry(url, options = {}, maxRetries = 5) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status !== 429) return response;
// Check Retry-After header first
const retryAfter = response.headers.get('Retry-After');
let delay;
if (retryAfter) {
delay = parseInt(retryAfter, 10) * 1000; // seconds to ms
} else {
// Exponential backoff: 1s, 2s, 4s, 8s, 16s
delay = Math.pow(2, attempt) * 1000;
}
// Add jitter (±20%) to prevent thundering herd
delay += delay * (Math.random() * 0.4 - 0.2);
console.log(`Rate limited. Retrying in ${Math.round(delay/1000)}s...`);
await new Promise(r => setTimeout(r, delay));
}
throw new Error('Max retries exceeded');
}
Python with requests
import time, random, requests
def fetch_with_retry(url, max_retries=5):
for attempt in range(max_retries):
response = requests.get(url)
if response.status_code != 429:
return response
retry_after = response.headers.get('Retry-After')
if retry_after:
delay = int(retry_after)
else:
delay = (2 ** attempt) + random.uniform(0, 1)
print(f"Rate limited. Waiting {delay:.1f}s before retry {attempt + 1}...")
time.sleep(delay)
raise Exception("Max retries exceeded")
Proactive Rate Limit Management
// Track your rate limit budget proactively
class RateLimitedClient {
constructor(requestsPerSecond) {
this.interval = 1000 / requestsPerSecond;
this.lastRequest = 0;
}
async request(url, options) {
const now = Date.now();
const elapsed = now - this.lastRequest;
if (elapsed < this.interval) {
await new Promise(r => setTimeout(r, this.interval - elapsed));
}
this.lastRequest = Date.now();
return fetch(url, options);
}
}
// Use: max 10 requests/second
const client = new RateLimitedClient(10);
await client.request('https://api.example.com/data');
Implementing Rate Limiting (Server Side)
Nginx
# Rate limit: 10 requests/second per IP
http {
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
server {
location /api/ {
limit_req zone=api burst=20 nodelay;
limit_req_status 429;
# Include rate limit headers
add_header X-RateLimit-Limit 10;
proxy_pass http://backend;
}
}
}
Express.js with express-rate-limit
const rateLimit = require('express-rate-limit');
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window
standardHeaders: true, // Return X-RateLimit-* headers
legacyHeaders: false,
handler: (req, res) => {
res.status(429).json({
error: 'Too many requests',
retry_after: Math.ceil(req.rateLimit.resetTime / 1000)
});
}
});
app.use('/api/', apiLimiter);
Frequently Asked Questions
What does HTTP 429 Too Many Requests mean?
It means you have exceeded the server's rate limit — you sent too many requests in too short a time. The server is asking you to slow down and try again later. Check the
Retry-After header for how long to wait, and look for X-RateLimit-* headers to understand your limits.How do I handle 429 errors in my code?
Use exponential backoff with jitter: wait 1s, then 2s, 4s, 8s between retries, adding random jitter to prevent all clients from retrying at the same time. Always respect the
Retry-After header if present. Proactively throttle your requests to stay under the limit rather than hitting 429 and retrying.How do I implement rate limiting on my API?
Use the token bucket or sliding window algorithm. Libraries like
express-rate-limit (Node.js), django-ratelimit (Python), or Nginx's limit_req module handle this. Always return X-RateLimit-Limit, X-RateLimit-Remaining, and Retry-After headers so clients can self-throttle. Store rate limit state in Redis for multi-server deployments.