JAVASCRIPT
Implementing Client-Side API Rate Limit Handling with Retry-After Header
Learn how to gracefully handle API rate limits on the client-side by parsing the `Retry-After` header and implementing a delayed retry mechanism.
async function fetchWithRateLimitHandling(url, options = {}, retries = 3) {
let currentRetries = 0;
while (currentRetries <= retries) {
try {
const response = await fetch(url, options);
if (response.status === 429) { // Too Many Requests
const retryAfterHeader = response.headers.get('Retry-After');
let delay = 1; // Default delay in seconds
if (retryAfterHeader) {
if (!isNaN(retryAfterHeader)) {
delay = parseInt(retryAfterHeader, 10);
} else {
// Handle HTTP-date format, e.g., "Retry-After: Fri, 31 Dec 1999 23:59:59 GMT"
const retryDate = new Date(retryAfterHeader);
const now = new Date();
if (retryDate > now) {
delay = Math.ceil((retryDate.getTime() - now.getTime()) / 1000); // Convert ms to s
}
}
}
console.warn(`Rate limit hit. Retrying after ${delay} seconds. Attempt ${currentRetries + 1} of ${retries + 1}.`);
await new Promise(resolve => setTimeout(resolve, delay * 1000));
currentRetries++;
continue; // Retry the request
}
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error("Error during fetch operation:", error);
// If it's not a 429 error or we've run out of retries, re-throw
if (error.message && error.message.includes('429') && currentRetries < retries) {
currentRetries++;
// For non-429 errors, we might want to just break or rethrow immediately
// For example, network error won't have response.status
console.warn(`Non-429 related error. Retrying attempt ${currentRetries} of ${retries}. Or breaking.`);
// For simplicity, this example only retries on 429. For other errors, you might want a different retry strategy.
await new Promise(resolve => setTimeout(resolve, 2000)); // Default small delay for other errors if retrying
continue;
} else {
throw error;
}
}
}
throw new Error(`Failed to fetch after ${retries + 1} attempts due to rate limiting or other errors.`);
}
// Example usage:
// fetchWithRateLimitHandling('https://api.example.com/protected-endpoint', { method: 'GET' }, 2)
// .then(data => console.log('Successfully fetched:', data))
// .catch(error => console.error('Final fetch failed:', error));
How it works: This JavaScript function demonstrates a robust client-side approach to handling API rate limits. It listens for a `429 Too Many Requests` status code and parses the `Retry-After` HTTP header to determine how long to wait before retrying the request. It includes a retry mechanism with a defined number of attempts, ensuring a smoother user experience even when facing temporary API constraints. This pattern helps prevent excessive API calls and makes your application more resilient.