API Rate Limiting Best Practices: How to Handle 429 Errors Gracefully
Alex Chen
Developer Advocate
Introduction
Every production API has rate limits. When your application hits a 429 "Too Many Requests" response, how you handle it determines whether your users see graceful degradation or a broken experience.
This guide covers robust rate limit handling with InstantAPI and any other API.
Understanding Rate Limit Headers
InstantAPI returns standard headers with every response:
- X-RateLimit-Limit — Maximum requests per window (100/minute)
- X-RateLimit-Remaining — Requests remaining in current window
- X-RateLimit-Reset — Unix timestamp when the window resets
- Retry-After — Seconds to wait before retrying (on 429 responses)
Strategy 1: Exponential Backoff with Jitter
Each retry waits exponentially longer, with random jitter to prevent thundering herd problems:
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status !== 429) return response;
if (attempt === maxRetries) throw new Error("Max retries exceeded");
const retryAfter = response.headers.get("Retry-After");
const baseDelay = retryAfter
? parseInt(retryAfter) * 1000
: Math.pow(2, attempt) * 1000;
const jitter = Math.random() * baseDelay * 0.5;
await new Promise(r => setTimeout(r, baseDelay + jitter));
}
}
Strategy 2: Proactive Rate Tracking
Track your usage proactively instead of waiting for errors:
class RateLimitTracker {
constructor() {
this.remaining = 100;
this.resetAt = Date.now();
}
updateFromHeaders(headers) {
this.remaining = parseInt(headers.get("X-RateLimit-Remaining") || "100");
this.resetAt = parseInt(headers.get("X-RateLimit-Reset") || "0") * 1000;
}
async waitIfNeeded() {
if (this.remaining <= 5) {
const waitMs = Math.max(0, this.resetAt - Date.now());
if (waitMs > 0) await new Promise(r => setTimeout(r, waitMs));
}
}
}
Strategy 3: Request Queue
For high-volume applications, queue requests at a controlled rate:
class RequestQueue {
constructor(maxPerMinute = 90) {
this.queue = [];
this.interval = (60 / maxPerMinute) * 1000;
this.processing = false;
}
enqueue(fn) {
return new Promise((resolve, reject) => {
this.queue.push({ fn, resolve, reject });
this.process();
});
}
async process() {
if (this.processing) return;
this.processing = true;
while (this.queue.length > 0) {
const { fn, resolve, reject } = this.queue.shift();
try { resolve(await fn()); } catch (err) { reject(err); }
await new Promise(r => setTimeout(r, this.interval));
}
this.processing = false;
}
}
InstantAPI Tips
InstantAPI's limit is 100 requests per minute per key. For higher throughput:
Ready to try InstantAPI?
Sign up today and get 10 free credits to explore all 6 AI capabilities. No credit card required.
Get 10 Free Credits