Axios vs ky in 2026: HTTP Clients for Modern JavaScript
·PkgPulse Team
TL;DR
Axios for Node.js-heavy backends; ky for modern browser/edge environments. Axios (~50M weekly downloads) is the default HTTP client for most Node.js projects — reliable, widely documented, interceptors, automatic JSON. ky (~4M downloads) is a ~5KB Fetch-based alternative that works in all modern environments including Cloudflare Workers and Deno. If you're targeting edge runtimes or want minimal bundle size, ky is compelling.
Key Takeaways
- Axios: ~50M weekly downloads — ky: ~4M (npm, March 2026)
- Axios uses XMLHttpRequest — ky wraps the native Fetch API
- ky is ~5KB gzipped — Axios is ~40KB gzipped
- Both support interceptors — ky's are promise-based
- ky works in edge runtimes — Axios has limited edge support
Basic Usage
// Axios — automatic JSON parsing, wide feature set
import axios from 'axios';
// GET with type safety
const { data } = await axios.get<User>('/api/user/123');
// data is User — typed
// POST with JSON body (automatic serialization)
const { data: newUser } = await axios.post<User>('/api/users', {
name: 'Alice',
email: 'alice@example.com',
});
// Error handling — throws on 4xx/5xx
try {
await axios.get('/api/missing');
} catch (err) {
if (axios.isAxiosError(err)) {
console.log(err.response?.status); // 404
console.log(err.response?.data); // Error response body
}
}
// ky — Fetch-based, similar API
import ky from 'ky';
// GET with JSON parsing
const user = await ky.get('/api/user/123').json<User>();
// .json() parses and types the response
// POST with JSON body
const newUser = await ky.post('/api/users', {
json: { name: 'Alice', email: 'alice@example.com' },
}).json<User>();
// Error handling — ky throws HTTPError on 4xx/5xx (unlike native fetch)
try {
await ky.get('/api/missing').json();
} catch (err) {
if (err instanceof ky.HTTPError) {
console.log(err.response.status); // 404
const body = await err.response.json(); // Async response body
}
}
Interceptors
// Axios — request/response interceptors
const api = axios.create({ baseURL: 'https://api.example.com' });
// Request interceptor — add auth token
api.interceptors.request.use((config) => {
config.headers.Authorization = `Bearer ${getToken()}`;
return config;
});
// Response interceptor — handle 401 refresh flow
api.interceptors.response.use(
(response) => response,
async (error) => {
if (error.response?.status === 401) {
await refreshToken();
return api.request(error.config); // Retry original request
}
return Promise.reject(error);
}
);
// ky — hooks (same concept, different API)
const api = ky.create({
prefixUrl: 'https://api.example.com',
hooks: {
beforeRequest: [
(request) => {
request.headers.set('Authorization', `Bearer ${getToken()}`);
},
],
afterResponse: [
async (request, options, response) => {
if (response.status === 401) {
await refreshToken();
return ky(request, options); // Retry
}
},
],
beforeRetry: [
({ request, error, retryCount }) => {
console.log(`Retry #${retryCount} for ${request.url}`);
},
],
},
});
Retry Logic
// ky — built-in retry with exponential backoff
const data = await ky.get('/api/flaky-endpoint', {
retry: {
limit: 3,
methods: ['get'],
statusCodes: [408, 413, 429, 500, 502, 503, 504],
backoffLimit: 3000, // Max delay between retries
},
}).json();
// Axios — no built-in retry (need axios-retry package)
import axiosRetry from 'axios-retry';
axiosRetry(axios, {
retries: 3,
retryDelay: axiosRetry.exponentialDelay,
});
Edge Runtime Support
// ky — works anywhere Fetch is available
// ✓ Cloudflare Workers
// ✓ Vercel Edge Functions
// ✓ Deno
// ✓ Bun
// ✓ Node.js 18+ (native Fetch)
// ✓ Browser
// Cloudflare Worker example:
export default {
async fetch(request) {
const data = await ky.get('https://api.external.com/data').json();
return new Response(JSON.stringify(data));
}
}
// Axios — limited edge support
// XMLHttpRequest not available in Workers
// Node.js 18+ adapter needed for native Fetch
// Generally not recommended for edge functions
When to Choose
Choose Axios when:
- Existing Node.js codebase with Axios already integrated
- Rich feature set out of the box (interceptors, transformers)
- Node.js 16 or below (before native Fetch)
- Large team where Axios's extensive docs/examples save onboarding time
- Browser support for IE11 required (ky requires modern Fetch)
Choose ky when:
- Targeting edge runtimes (Cloudflare Workers, Vercel Edge)
- Bundle size matters (ky is 8x smaller than Axios)
- Modern JavaScript environment (all runtimes supporting native Fetch)
- You want built-in retry logic
- Migrating away from axios to a leaner alternative
Compare Axios and ky package health on PkgPulse.
See the live comparison
View axios vs. ky on PkgPulse →