Handle HTTP 429 Too Many Requests in Axios
Handling an HTTP 429 Too Many Requests error in Axios is essential
for building resilient applications that interact with rate-limited
APIs. This guide outlines how to detect 429 errors, respect standard
rate-limiting headers like Retry-After, and automatically
retry failed requests using both custom Axios interceptors and the
popular axios-retry library.
Understanding the 429 Status Code
An HTTP 429 status code indicates that the client has sent too many
requests in a given amount of time. Most well-designed APIs include a
Retry-After response header indicating how many seconds (or
an explicit HTTP date) the client should wait before making another
request.
Method 1:
Using the axios-retry Plugin (Recommended)
The most straightforward way to handle 429 responses is using the
axios-retry package. It provides built-in exponential
backoff and custom retry conditions.
1. Install the package
npm install axios axios-retry2. Configure Axios with Automatic Retries
const axios = require('axios');
const axiosRetry = require('axios-retry');
const client = axios.create({ baseURL: 'https://api.example.com' });
axiosRetry(client, {
retries: 3, // Number of retry attempts
retryDelay: (retryCount, error) => {
// Check if the server sent a Retry-After header
const retryAfter = error.response?.headers['retry-after'];
if (retryAfter) {
return parseInt(retryAfter, 10) * 1000;
}
// Fall back to exponential backoff
return axiosRetry.exponentialDelay(retryCount);
},
retryCondition: (error) => {
// Retry only if status code is 429
return error.response?.status === 429;
},
});
async function fetchData() {
try {
const response = await client.get('/data');
console.log(response.data);
} catch (error) {
console.error('Request failed after retries:', error.message);
}
}
fetchData();Method 2: Custom Axios Response Interceptor
If you prefer not to install external dependencies, you can implement retry logic using Axios response interceptors.
const axios = require('axios');
const client = axios.create();
// Helper to delay execution
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
client.interceptors.response.use(
(response) => response,
async (error) => {
const { config, response } = error;
// Check if error is 429 and retry count is not exceeded
if (response && response.status === 429) {
config.__retryCount = config.__retryCount || 0;
const maxRetries = 3;
if (config.__retryCount < maxRetries) {
config.__retryCount += 1;
// Parse Retry-After header or calculate exponential backoff
const retryAfterHeader = response.headers['retry-after'];
let delay = Math.pow(2, config.__retryCount) * 1000; // Default exponential backoff
if (retryAfterHeader) {
delay = !isNaN(retryAfterHeader)
? parseInt(retryAfterHeader, 10) * 1000
: Math.max(0, new Date(retryAfterHeader).getTime() - Date.now());
}
console.warn(`429 received. Retrying attempt #${config.__retryCount} in ${delay}ms...`);
await sleep(delay);
// Re-execute original request
return client(config);
}
}
return Promise.reject(error);
}
);Best Practices for Handling 429 Responses
- Always Read
Retry-After: Check for theRetry-Afterheader before applying generic delay algorithms. - Use Exponential Backoff with Jitter: When multiple clients encounter rate limits simultaneously, adding random "jitter" to backoff intervals prevents them from hitting the server again at the exact same moment.
- Proactive Client-Side Throttling: For
high-throughput applications, implement local rate limiters (such as
token bucket or queue managers like
bottleneckorp-queue) to avoid exceeding server thresholds in the first place.