Respect Retry-After on 429 and 503 responses
Retry rate-limited requests after the delay the server asks for, instead of guessing.
- JavaScript
- GreenSnippets
- MIT
- Executed with Node.js 22
function retryAfterMs(response, now = Date.now()) {
const header = response.headers.get("retry-after");
if (!header) return null;
const seconds = Number(header);
if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);
const date = Date.parse(header);
return Number.isNaN(date) ? null : Math.max(0, date - now);
}
async function fetchWithRetryAfter(url, init, { retries = 3, maxWaitMs = 30_000, sleep = (ms) => new Promise((r) => setTimeout(r, ms)) } = {}) {
for (let attempt = 0; ; attempt++) {
const response = await fetch(url, init);
if (![429, 503].includes(response.status) || attempt >= retries) return response;
const wait = retryAfterMs(response) ?? 500 * 2 ** attempt;
await sleep(Math.min(wait, maxWaitMs));
}
}Example / expected output
const res = await fetchWithRetryAfter("https://api.example.com/items");How it works
When an API answers 429 Too Many Requests or 503, the Retry-After header says how long to wait, either in seconds or as an HTTP date. Honoring it is both faster and friendlier than blind backoff. Cap the wait and the number of retries so a bad server cannot stall you forever.
Related snippets
- TypeScript Tested
Typed fetch helper that checks the response
A fetch wrapper that throws on HTTP errors and validates the JSON body with a type guard.
- Python Tested
Token bucket rate limiter
Allow bursts but cap the sustained request rate with a small thread-safe token bucket.
- Python Tested
Sign API requests with HMAC-SHA256
Create and verify a timestamped signature over the method, path and body so requests cannot be forged or replayed.
- JavaScript Tested
Build RFC 9457 problem details error responses
Return consistent, machine-readable API errors using the application/problem+json format.

