This comparison evaluates five npm packages designed to handle transient network failures by automatically retrying failed HTTP requests. axios-retry and retry-axios are plugins specifically for the Axios library, offering different levels of control and maintenance status. fetch-retry wraps the native fetch API to add retry capabilities without replacing the underlying client. got is a complete, human-friendly HTTP client for Node.js with built-in retry logic. superagent-retry extends the Superagent library, though both the plugin and its parent library face significant maintenance challenges. These tools help developers build resilient applications that can recover from temporary network glitches, 5xx server errors, or rate limiting without manual intervention.
Network requests fail. It is not a matter of "if," but "when." Whether it is a flaky mobile connection, a temporary server overload (503), or a rate limit (429), professional applications must handle these hiccups gracefully. Relying on manual try...catch loops is error-prone and clutters your business logic.
This analysis compares five specific solutions for automating retries: axios-retry, fetch-retry, got, retry-axios, and superagent-retry. We will look at how they integrate, how they handle timing, and which ones you should actually put into production today.
The first major difference is how these tools attach to your network layer. Some are plugins for existing clients, one is a built-in feature, and another is a wrapper around the native API.
axios-retry acts as an interceptor plugin. You install it alongside Axios and register it once. It hooks into Axios's internal promise chain.
// axios-retry setup
import axios from 'axios';
import axiosRetry from 'axios-retry';
const client = axios.create();
axiosRetry(client, { retries: 3 });
// Usage remains standard Axios
client.get('https://api.example.com/data');
retry-axios also uses the interceptor pattern but attaches to the global Axios instance or a specific one via a slightly different API structure. It modifies the request config before it goes out.
// retry-axios setup
import axios from 'axios';
import { attach } from 'retry-axios';
attach({ axiosInstance: axios });
// Usage remains standard Axios
axios.get('https://api.example.com/data');
fetch-retry is a wrapper function. It does not modify the global fetch; instead, it returns a new function that you use in place of the native one. This keeps the global scope clean.
// fetch-retry setup
import fetchRetry from 'fetch-retry';
const fetchWithRetry = fetchRetry(fetch);
// Usage replaces native fetch
fetchWithRetry('https://api.example.com/data', { retries: 3 })
.then(res => res.json());
got requires no plugins. It is a complete HTTP client where retry logic is a core configuration option passed directly into the request.
// got setup (no plugin needed)
import got from 'got';
// Usage includes retry config directly
const response = await got('https://api.example.com/data', {
retry: { limit: 3 }
});
superagent-retry extends the Superagent request prototype. You call .retry() explicitly on the request chain, similar to how you might call .timeout().
// superagent-retry setup
import request from 'superagent';
import requestRetry from 'superagent-retry';
requestRetry(request);
// Usage chains the retry method
request
.get('https://api.example.com/data')
.retry(3)
.end((err, res) => { /* handle */ });
When a request fails, hammering the server immediately often makes things worse. Good retry logic uses "backoff" — waiting longer between each attempt. Let's see how each package handles this.
axios-retry provides a built-in exponential backoff function but allows you to write your own. This is crucial for respecting rate limits.
// axios-retry: Custom delay calculation
axiosRetry(client, {
retryDelay: (retryCount) => {
return retryCount * 1000; // Linear delay: 1s, 2s, 3s
}
});
retry-axios supports a similar configuration where you define the delay logic based on the attempt number.
// retry-axios: Custom delay
attach({
axiosInstance: axios,
retryConfig: {
retryDelay: (count, error) => {
return Math.pow(2, count) * 100; // Exponential
}
}
});
fetch-retry lets you pass a retryDelay function in the options object. It is straightforward and functional.
// fetch-retry: Custom delay
fetchWithRetry(url, {
retryDelay: function(attempt) {
return Math.pow(2, attempt) * 1000;
}
});
got has a very sophisticated built-in backoff strategy by default (exponential with jitter). You can override it, but the defaults are often sufficient for production use.
// got: Overriding default backoff
got(url, {
retry: {
calculateDelay: ({ attemptCount }) => {
return attemptCount * 500;
}
}
});
superagent-retry typically uses a fixed delay or a simple exponential strategy internally, with less flexibility for custom algorithms compared to the Axios plugins.
// superagent-retry: Fixed delay usage
request
.get(url)
.retry(3, 1000) // Retries 3 times with 1000ms delay
.end(callback);
Not every error deserves a retry. A 404 (Not Found) will never fix itself by trying again. You only want to retry on network errors, 5xx server issues, or 429 (Too Many Requests).
axios-retry exports a helper isNetworkOrIdempotentRequestError but also lets you write a boolean function to decide.
// axios-retry: Custom condition
axiosRetry(client, {
retryCondition: (error) => {
// Retry only on 500 errors or network issues
return error.response?.status === 500 || error.code === 'ECONNRESET';
}
});
retry-axios uses a similar retryCondition callback in its configuration object.
// retry-axios: Custom condition
attach({
retryConfig: {
retryCondition: (error) => {
return error.config && error.config.method === 'get';
}
}
});
fetch-retry provides a retryOn function. Since fetch only rejects on network failure (not HTTP error statuses), you often need to check the response status inside this function.
// fetch-retry: Check response status
fetchWithRetry(url, {
retryOn: async (attempt, error, response) => {
if (attempt >= 3) return false;
if (response && response.status === 503) return true;
return false;
}
});
got allows you to specify which HTTP status codes trigger a retry via the statusCodes array, alongside network errors.
// got: Specific status codes
got(url, {
retry: {
limit: 3,
statusCodes: [408, 413, 429, 500, 502, 503, 504]
}
});
superagent-retry generally retries on network errors and specific server errors by default, but customizing this logic deeply is more verbose than in the Axios ecosystem.
// superagent-retry: Basic usage
// Primarily relies on the count argument;
// complex filtering often requires manual wrapping
request.get(url).retry(3).end(...);
This is the most critical section for architectural decisions. Using unmaintained software introduces security risks and compatibility bugs.
retry-axios has historically suffered from long gaps between releases and issues left unanswered by maintainers. While it still works, the community has largely migrated to axios-retry, which is more actively updated and has broader adoption. Recommendation: Do not start new projects with retry-axios. Use axios-retry instead.
superagent-retry depends on Superagent, a library that has seen a massive decline in usage in favor of Axios and native Fetch. The plugin itself sees very little activity. Recommendation: Avoid for new projects. If you are stuck on Superagent, plan a migration to Axios or Fetch.
axios-retry, fetch-retry, and got are all actively maintained. They receive regular updates to support new Node.js versions, fix security vulnerabilities, and improve TypeScript definitions. These are the safe choices for 2024 and beyond.
Where your code runs dictates which tool you can use.
got is Node.js only. It relies on Node-specific streams and HTTP agents. It will not work in a browser bundle without heavy (and often broken) shimming.axios-retry and retry-axios work wherever Axios works: both Browser and Node.js. This makes them perfect for isomorphic applications (code shared between server and client).fetch-retry works in any environment that supports fetch. This includes all modern browsers and Node.js 18+. For older Node versions, you need a polyfill like node-fetch.superagent-retry works in both, but again, the parent library is heavy for browsers compared to native fetch.// Example: Isomorphic retry with axios-retry
// Works in React (Browser) and Next.js API routes (Node)
import axios from 'axios';
import axiosRetry from 'axios-retry';
const api = axios.create();
axiosRetry(api, { retries: 3 });
export default api;
// Example: Node-only high-performance script with got
// Will crash if imported in a browser bundle
import got from 'got';
async function scrapeData() {
return await got('https://api.data.com', { retry: { limit: 5 } });
}
| Feature | axios-retry | fetch-retry | got | retry-axios | superagent-retry |
|---|---|---|---|---|---|
| Base Client | Axios | Native Fetch | Built-in | Axios | Superagent |
| Environment | Browser & Node | Browser & Node (18+) | Node Only | Browser & Node | Browser & Node |
| Setup Style | Plugin / Interceptor | Wrapper Function | Built-in Config | Plugin / Interceptor | Prototype Extension |
| Custom Delays | ✅ Full Control | ✅ Full Control | ✅ Full Control | ✅ Full Control | ⚠️ Limited |
| Maintenance | 🟢 Active | 🟢 Active | 🟢 Active | 🟡 Low Activity | 🔴 Legacy / Low Activity |
| Bundle Size | Medium (depends on Axios) | Tiny | Large (Node libs) | Medium (depends on Axios) | Large |
Your choice should depend on your existing stack and target environment:
got. It is the most powerful, has the best developer experience, and requires no extra plugins. Its built-in retry logic is robust and battle-tested.axios-retry. Axios remains the standard for complex frontend HTTP needs (interceptors, cancellation, broad browser support), and this plugin integrates perfectly.fetch-retry. If you are done with Axios and want to use the native fetch API, this is the simplest way to add reliability without bloating your bundle.retry-axios or superagent-retry for new architecture. The risk of unmaintained dependencies outweighs any minor API preferences they might offer. Stick to the actively maintained leaders in the ecosystem.Choose got if you are building a Node.js-only application and want a powerful, all-in-one HTTP client. Unlike the others, it does not require a separate plugin for retries; the functionality is built-in and highly configurable. It is best for backend services, scripts, and tools where developer experience and comprehensive features are prioritized over bundle size.
Choose axios-retry if your project already relies on Axios and you need a actively maintained, feature-rich plugin. It offers excellent control over retry conditions, custom delays, and works seamlessly in both Node.js and browser environments. It is the standard choice for modern Axios-based stacks requiring robust error recovery.
Choose fetch-retry if you prefer using the native fetch API and want to avoid adding a heavy HTTP client like Axios or Got. It is ideal for modern browsers and Node.js 18+ environments where you want lightweight retry logic that simply wraps the existing global fetch function without changing your request syntax significantly.
Avoid choosing retry-axios for new projects. While it functions as an interceptor for Axios similar to axios-retry, it has historically suffered from long periods of inactivity and lack of maintenance compared to its competitor. Unless you are maintaining a legacy codebase that strictly depends on it, axios-retry is the safer, more reliable alternative.
Avoid choosing superagent-retry for new projects. Both this plugin and its parent library, Superagent, are largely considered legacy in the modern JavaScript ecosystem. They lack the active development and community support found in Axios or native fetch solutions. Migrating to axios-retry or fetch-retry is recommended for better long-term stability.
Sindre's open source work is supported by the community.
Special thanks to:
Human-friendly and powerful HTTP request library for Node.js
See how Got compares to other HTTP libraries
You probably want Ky instead, by the same people. It's smaller, works in the browser too, and is more stable since it's built on Fetch. Or fetch-extras for simple needs.
Support questions should be asked here.
npm install got
Warning: This package is native ESM and no longer provides a CommonJS export. If your project uses CommonJS, you will have to convert to ESM. Please don't open issues for questions regarding CommonJS / ESM.
Got v11 is no longer maintained and we will not accept any backport requests.
A quick start guide is available.
Got has a dedicated option for handling JSON payload.
Furthermore, the promise exposes a .json<T>() function that returns Promise<T>.
import got from 'got';
const {data} = await got.post('https://httpbin.org/anything', {
json: {
hello: 'world'
}
}).json();
console.log(data);
//=> {"hello": "world"}
For advanced JSON usage, check out the parseJson and stringifyJson options.
For more useful tips like this, visit the Tips page.
By default, Got will retry on failure. To disable this option, set options.retry.limit to 0.
got4aws - Got convenience wrapper to interact with AWS v4 signed APIsgh-got - Got convenience wrapper to interact with the GitHub APIgl-got - Got convenience wrapper to interact with the GitLab APIgotql - Got convenience wrapper to interact with GraphQL using JSON-parsed queries instead of stringsgot-fetch - Got with a fetch interfacegot-scraping - Got wrapper specifically designed for web scraping purposesgot-ssrf - Got wrapper to protect server-side requests against SSRF attacksgot | node-fetch | ky | axios | superagent | |
|---|---|---|---|---|---|
| HTTP/2 support | :heavy_check_mark: | :x: | :heavy_check_mark: | :x: | :heavy_check_mark:** |
| Browser support | :x: | :heavy_check_mark:* | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Promise API | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Stream API | :heavy_check_mark: | Node.js only | :x: | :x: | :heavy_check_mark: |
| Pagination API | :heavy_check_mark: | :x: | :x: | :x: | :x: |
| Request aborting | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| RFC 7234 caching | :heavy_check_mark: | :x: | :x: | :x: | :x: |
| Cookies (out-of-the-box) | :heavy_check_mark: | :x: | :x: | :x: | :x: |
| Follows redirects | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Retries on failure | :heavy_check_mark: | :x: | :heavy_check_mark: | :x: | :heavy_check_mark: |
| Progress events | :heavy_check_mark: | :x: | :heavy_check_mark: | Browser only | :heavy_check_mark: |
| Handles gzip/deflate | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Advanced timeouts | :heavy_check_mark: | :x: | :x: | :x: | :x: |
| Timings | :heavy_check_mark: | :x: | :x: | :x: | :x: |
| Errors with metadata | :heavy_check_mark: | :x: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| JSON mode | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Custom defaults | :heavy_check_mark: | :x: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| Composable | :heavy_check_mark: | :x: | :x: | :x: | :heavy_check_mark: |
| Hooks | :heavy_check_mark: | :x: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| Issues open | |||||
| Issues closed | |||||
| Downloads | |||||
| Coverage | TBD | ||||
| Build | |||||
| Bugs | |||||
| Dependents | |||||
| Install size | |||||
| GitHub stars | |||||
| TypeScript support | |||||
| Last commit |
* It's almost API compatible with the browser fetch API.
** Need to switch the protocol manually. Doesn't accept PUSH streams and doesn't reuse HTTP/2 sessions.
:sparkle: Almost-stable feature, but the API may change. Don't hesitate to try it out!
:grey_question: Feature in early stage of development. Very experimental.
Click here to see the install size of the Got dependencies.
![]() | ![]() |
|---|---|
| Sindre Sorhus | Szymon Marczak |
|
|
|
|
|
|
|
|
|
|
Segment is a happy user of Got! Got powers the main backend API that our app talks to. It's used by our in-house RPC client that we use to communicate with all microservices.
Antora, a static site generator for creating documentation sites, uses Got to download the UI bundle. In Antora, the UI bundle (aka theme) is maintained as a separate project. That project exports the UI as a zip file we call the UI bundle. The main site generator downloads that UI from a URL using Got and streams it to vinyl-zip to extract the files. Those files go on to be used to create the HTML pages and supporting assets.
GetVoIP is happily using Got in production. One of the unique capabilities of Got is the ability to handle Unix sockets which enables us to build a full control interfaces for our docker stack.
We're using Got inside of Exoframe to handle all the communication between CLI and server. Exoframe is a self-hosted tool that allows simple one-command deployments using Docker.
Karaoke Mugen uses Got to fetch content updates from its online server.
Renovate uses Got, gh-got and gl-got to send millions of queries per day to GitHub, GitLab, npmjs, PyPi, Packagist, Docker Hub, Terraform, CircleCI, and more.
Resistbot uses Got to communicate from the API frontend where all correspondence ingresses to the officials lookup database in back.
Natural Cycles is using Got to communicate with all kinds of 3rd-party REST APIs (over 9000!).
Microlink is a cloud browser as an API service that uses Got widely as the main HTTP client, serving ~22M requests a month, every time a network call needs to be performed.
We’re using Got at Radity. Thanks for such an amazing work!