Skip to main content

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 →

Comments

Stay Updated

Get the latest package insights, npm trends, and tooling tips delivered to your inbox.