Client-Side Rate Limiting in Axios
Client-side rate limiting prevents applications from overwhelming
APIs, avoiding 429 Too Many Requests errors and staying
within usage quotas. While the Axios HTTP client does not have native,
out-of-the-box rate limiting functionality, it provides powerful
primitives—primarily interceptors and custom adapters—that allow
developers to implement robust rate limiting, request throttling, token
bucket algorithms, and automatic retry strategies seamlessly.
Axios Interceptors and Request Queuing
Axios interceptors are the primary mechanism for intercepting and altering requests before they are sent, or responses before they are processed. To implement rate limiting directly within Axios, you can use a request interceptor combined with a queue.
When a request is initiated, the interceptor can push the request execution into a delay mechanism or queue rather than executing it immediately:
import axios from 'axios';
const apiClient = axios.create({ baseURL: 'https://api.example.com' });
let lastRequestTime = 0;
const MIN_INTERVAL_MS = 200; // Limit to 5 requests per second
apiClient.interceptors.request.use(async (config) => {
const now = Date.now();
const timeSinceLast = now - lastRequestTime;
if (timeSinceLast < MIN_INTERVAL_MS) {
const delay = MIN_INTERVAL_MS - timeSinceLast;
await new Promise((resolve) => setTimeout(resolve, delay));
}
lastRequestTime = Date.now();
return config;
});Third-Party Throttling Libraries
For complex rate limiting—such as managing concurrent request limits,
burst allowances, and reservoir refreshes—integrating specialized
concurrency managers like bottleneck or
p-throttle with Axios is standard practice.
Using Bottleneck
Bottleneck acts as a task scheduler that wraps Axios calls to ensure adherence to strict rate limits:
import axios from 'axios';
import Bottleneck from 'bottleneck';
const limiter = new Bottleneck({
minTime: 250, // 4 requests per second
maxConcurrent: 2 // Maximum 2 requests running simultaneously
});
const apiClient = axios.create();
// Wrap the Axios request call
export const throttledGet = limiter.wrap((url, config) => apiClient.get(url, config));Using Axios Rate Limit Plugins
Packages such as axios-rate-limit wrap an existing Axios
instance directly, applying a token bucket or sliding window algorithm
behind the scenes without modifying how API calls are made across the
codebase:
import axios from 'axios';
import rateLimit from 'axios-rate-limit';
// Creates an Axios instance that allows a maximum of 10 requests per 1000ms
const http = rateLimit(axios.create(), {
maxRequests: 10,
perMilliseconds: 1000,
maxRPS: 10
});Handling 429 Responses and Exponential Backoff
Client-side rate limiting should also include reactive strategies
when the server signals that limits have been breached. Using
axios-retry or custom response interceptors allows Axios to
respect server-provided Retry-After headers and apply
exponential backoff:
import axios from 'axios';
import axiosRetry from 'axios-retry';
const apiClient = axios.create();
axiosRetry(apiClient, {
retries: 3,
retryCondition: (error) => error.response?.status === 429,
retryDelay: (retryCount, error) => {
const retryAfter = error.response?.headers['retry-after'];
if (retryAfter) {
return parseInt(retryAfter, 10) * 1000;
}
return axiosRetry.exponentialDelay(retryCount);
}
});Summary of Strategies
- Proactive Throttling: Intercept requests to space them out evenly before transmission.
- Concurrency Control: Limit the number of in-flight requests using queues.
- Reactive Backoff: Read
Retry-Afterheaders on429responses to pause and reschedule dropped requests.