Strategies and headers for API rate limiting.
X-RateLimit-Limit: 1000 # Max requests per window
X-RateLimit-Remaining: 847 # Requests remaining
X-RateLimit-Reset: 1698415200 # Unix timestamp when limit resets
Retry-After: 60 # Seconds to wait (on 429)
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests",
"retry_after": 60,
"limit": 1000,
"remaining": 0,
"reset_at": "2024-10-27T12:00:00Z"
}
}
Simple: count requests in fixed time periods.
Window: 1 minute (00:00-00:59, 01:00-01:59, ...)
Limit: 100 requests
Pros: Simple to implement
Cons: Burst at window edges (200 in 2 seconds across boundary)
Smoother: use weighted average across windows.
Current window: 50% through
Previous window: 60 requests
Current window: 40 requests
Weighted count = (60 × 0.5) + 40 = 70
Remaining = 100 - 70 = 30
Pros: Smoother limits
Cons: More complex, needs previous window data
Allows bursts with steady refill.
Bucket capacity: 100 tokens
Refill rate: 10 tokens/second
- Start with 100 tokens
- Each request costs 1 token
- Tokens refill at 10/sec
- Burst allowed up to 100, then steady 10/sec
Pros: Allows bursts, intuitive
Cons: More state to track
Fixed output rate, queue excess.
Processing rate: 10 requests/second
Queue size: 50
- Requests queue up
- Processed at constant rate
- Queue overflow = 429
Pros: Smooth output, protects backend
Cons: Adds latency
# Free tier
X-RateLimit-Limit: 100
X-RateLimit-Window: 3600 # per hour
# Pro tier
X-RateLimit-Limit: 10000
X-RateLimit-Window: 3600
# Search (expensive)
X-RateLimit-Limit: 10
X-RateLimit-Window: 60
# Read (cheap)
X-RateLimit-Limit: 1000
X-RateLimit-Window: 60
# Writes
POST/PUT/DELETE: 100/minute
# Reads
GET: 1000/minute
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 4999
X-RateLimit-Reset: 1372700873
X-RateLimit-Used: 1
X-RateLimit-Resource: core
RateLimit-Limit: 100
RateLimit-Remaining: 50
RateLimit-Reset: 60
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After') || 60;
await sleep(retryAfter * 1000);
continue;
}
return response;
}
throw new Error('Rate limit exceeded after retries');
}
function checkRateLimit(response) {
const remaining = response.headers.get('X-RateLimit-Remaining');
const reset = response.headers.get('X-RateLimit-Reset');
if (remaining < 10) {
const waitMs = (reset - Date.now()) / remaining;
// Slow down requests
}
}
Retry-AfterRetry-After header