How to Retry Failed Requests in Axios

Implementing a retry mechanism for failed HTTP requests in Axios ensures your application can recover gracefully from temporary network drops, rate limits, and transient server errors. This article outlines the essential steps to configure automatic retries in Axios using both the popular axios-retry plugin for a production-ready solution and custom Axios interceptors for a lightweight, dependency-free approach.


The most robust way to handle retries is with the official axios-retry library, which provides built-in support for exponential backoff, custom retry conditions, and retry delays.

Step 1: Install the Package

Install both Axios and the axios-retry package via your package manager:

npm install axios axios-retry

Step 2: Configure the Plugin on an Axios Instance

Create an Axios instance and attach the retry configuration using axiosRetry:

import axios from 'axios';
import axiosRetry from 'axios-retry';

const apiClient = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 5000,
});

// Configure retry behavior
axiosRetry(apiClient, {
  retries: 3, // Number of retry attempts
  retryDelay: axiosRetry.exponentialDelay, // Exponential backoff (e.g., 100ms, 200ms, 400ms)
  retryCondition: (error) => {
    // Retry on standard network errors or 5xx server errors
    return (
      axiosRetry.isNetworkOrIdempotentRequestError(error) ||
      error.response?.status === 429 // Also retry on rate limits
    );
  },
  onRetry: (retryCount, error, requestConfig) => {
    console.log(`Retry attempt #${retryCount} for URL: ${requestConfig.url}`);
  },
});

export default apiClient;

Method 2: Manual Implementation with Axios Interceptors

If you prefer not to install external dependencies, you can implement retry logic manually using Axios response interceptors.

Step 1: Create an Axios Instance

Define your base instance and default configuration settings:

import axios from 'axios';

const apiClient = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 5000,
});

Step 2: Attach a Response Interceptor

Use interceptors.response.use to capture errors, track retry counts on the request configuration object, and resend the request when appropriate:

apiClient.interceptors.response.use(
  (response) => response,
  async (error) => {
    const { config } = error;

    // Do not retry if config is missing or retries are not enabled
    if (!config || !config.retry) {
      return Promise.reject(error);
    }

    // Set retry state
    config.__retryCount = config.__retryCount || 0;

    // Check if max retries have been reached
    if (config.__retryCount >= config.retry) {
      return Promise.reject(error);
    }

    // Only retry on network errors or 5xx server status codes
    const shouldRetry =
      !error.response || (error.response.status >= 500 && error.response.status <= 599);

    if (!shouldRetry) {
      return Promise.reject(error);
    }

    // Increment retry count
    config.__retryCount += 1;

    // Calculate exponential delay
    const delay = config.retryDelay || 1000;
    const backoff = delay * Math.pow(2, config.__retryCount - 1);

    await new Promise((resolve) => setTimeout(resolve, backoff));

    // Re-execute the request
    return apiClient(config);
  }
);

Step 3: Trigger Requests with Retry Options

Pass custom retry parameters directly inside your request config:

apiClient.get('/data', {
  retry: 3,        // Retry up to 3 times
  retryDelay: 1000 // Base delay of 1 second
})
.then(response => console.log(response.data))
.catch(error => console.error('Request failed after retries:', error.message));

Essential Best Practices