Node.js HTTP Clients Compared 2026 — undici, fetch, axios, got, ky
product-development12 min readintermediate

Node.js HTTP Clients in 2026: Axios vs Fetch vs Undici

Vivek Singh
Founder & CEO at Witarist · May 29, 2026

Choosing an HTTP client used to be a one-line decision: install axios, move on. In 2026 the calculus changed. Node.js 22 ships fetch as a stable global, undici 7 reuses connection pools by default, and HTTP/2 + early HTTP/3 support is no longer optional for high-throughput services. The wrong client choice now costs you real throughput, real memory, and real dollars in cloud spend.

This guide compares the five HTTP clients you will actually consider when building a Node.js backend in 2026 — undici, the built-in fetch, axios, got, and ky — across raw performance, developer experience, TypeScript inference, ecosystem maturity, and operational footprint. We benchmarked each on identical workloads, scored them on a developer-experience panel, and translated the results into a single decision flow you can use in your next code review.

The 2026 Node.js HTTP client landscape

Five clients matter today. undici is the low-level engine that Node.js itself uses to implement fetch — direct use unlocks connection pooling, HTTP/2 multiplexing, dispatcher hooks, and the lowest latency in the ecosystem. The built-in global fetch is the cross-runtime standard: identical code on Node, Bun, Deno, Cloudflare Workers, and the browser. axios remains the most-installed (≈54M weekly downloads on npm) thanks to interceptors and a familiar API, but the underlying transport is showing its age.

got is the specialist library for retries, streams, and Node-specific power features. ky is a tiny (~5KB) fetch wrapper with a clean API, ideal for client-side SDKs and short-lived workers. The chart below shows the headline number — throughput in requests per second on an identical k6 workload — and quickly reveals why "just use axios" stopped being good advice.

Why this matters for your stack

Throughput translates directly to compute cost. A 2× faster HTTP client at the API gateway tier means half the EC2 instances or half the serverless invocation seconds. When we audit codebases at HireNodeJS, swapping a hot-path axios client for undici is one of the most reliable single-PR wins we see — typically 30–60% lower p95 latency with no behavioral change for callers.

Bar chart comparing requests per second across Node.js HTTP clients undici, fetch, got, ky, and axios in 2026 benchmarks
Figure 1 — Throughput at 500 concurrent users on Node.js 22 LTS. undici and the built-in fetch lead by 2× over axios on identical k6 workloads.

undici: the throughput champion

undici is the HTTP/1.1, HTTP/2, and (experimentally) HTTP/3 client written by the Node.js core team. It is what powers global fetch under the hood. Using undici directly trades the polished fetch API for raw control: you get persistent connection pools per origin, HTTP/2 stream multiplexing, request pipelining, and a dispatcher pattern that lets you intercept, retry, mock, or proxy any request in one place.

When undici is the right call

Reach for undici when you are building service-to-service traffic that lives inside a single Node process for the lifetime of the container — API gateways, BFFs, fan-out aggregators, webhook senders, and any worker that calls the same upstream thousands of times per second. The pool reuse alone usually cuts socket churn by 80% and reduces TLS handshake overhead to near zero.

http-client.js
import { Agent, request, setGlobalDispatcher } from 'undici'

// One pool per origin, reused across the whole process.
setGlobalDispatcher(new Agent({
  connections: 100,           // sockets per origin
  pipelining: 10,             // HTTP/1.1 pipelining depth
  keepAliveTimeout: 30_000,
  keepAliveMaxTimeout: 600_000,
  connect: { rejectUnauthorized: true }
}))

// Now every fetch() AND undici.request() share the same warm pool.
export async function getUser(id) {
  const { body, statusCode } = await request(`https://api.upstream.io/users/${id}`, {
    method: 'GET',
    headers: { authorization: `Bearer ${process.env.UPSTREAM_TOKEN}` }
  })
  if (statusCode >= 400) throw new Error(`Upstream ${statusCode}`)
  return await body.json()
}
🚀Pro Tip
Even if you keep writing fetch() everywhere, calling setGlobalDispatcher(new Agent(...)) once at boot upgrades every fetch call in your codebase to use a tuned pool. This is the single highest-ROI change you can make to a Node.js backend in 2026.
Figure 2 — Interactive: throughput, p95 latency and RSS memory across the five clients. Hover any bar for exact numbers.

Built-in fetch: the cross-runtime default

Global fetch in Node.js 22+ is no longer experimental. It is the same WHATWG spec the browser implements, backed by undici under the hood. The single biggest reason to use it: code portability. The exact same request function will run on Node, Bun, Deno, Cloudflare Workers, Vercel Edge Functions, and Lambda@Edge with no changes — and your TypeScript types come from `lib.dom` / `lib.webworker` instead of a third-party `@types` package that drifts out of sync.

The trade-offs you accept

Built-in fetch does not give you the rich Node-specific knobs that undici and got expose — there is no first-class request retry, no streaming progress hook, no easy way to swap a TLS cert mid-test. If you need any of those, you either reach for undici (and lose cross-runtime portability) or wrap fetch with ky / a small custom helper. The radar chart below shows where each client lands on the dimensions that come up in real code reviews.

Decision flowchart for picking the right Node.js HTTP client in 2026: undici for throughput, axios for legacy, fetch for edge
Figure 3 — Decision flow. Start from the green node and follow the arrows; the FOCUS card is the default pick for the matching constraint.

axios: still the right tool for legacy and SDKs

axios 1.7 is not obsolete. It is, however, no longer the obvious default. The reasons it stays in our toolkit are specific: a battle-tested interceptor model, `defaults` for shared config, automatic JSON encoding/decoding, request cancellation via AbortController, and a huge corpus of internal SDKs that depend on its exact request/response shape. Ripping axios out of a 100k-LOC codebase to chase a 10% latency win is rarely worth it.

Use axios when you have interceptors

Ready to build your team?

Hire Pre-Vetted Node.js Developers

Skip the months-long search. Our exclusive talent network has senior Node.js experts ready to join your team in 48 hours.

Authentication refresh, distributed tracing headers, request/response logging — these are the patterns that axios still handles more cleanly than fetch. If you are starting fresh today you can replicate them on top of undici or fetch in 40 lines, but if you already have them, the migration cost outweighs the benefit.

⚠️Warning
Do not use axios in serverless cold-start hot paths. The package is ~110KB unpacked and adds 30–80ms to Lambda cold starts compared to native fetch. For Lambda, Workers, or any edge runtime, prefer fetch or ky.

got and ky: the specialist toolbox

got 14 is the answer when you need Node-specific power features that fetch cannot express cleanly: built-in exponential backoff with jitter, hooks that fire at every stage of the request lifecycle, native stream support for downloads, automatic decompression, and pagination helpers for cursor-based APIs. It is heavier than fetch (≈200KB), so weigh it against your bundle and cold-start budget.

ky is the opposite trade — a 5KB fetch wrapper that adds retries, timeouts, JSON helpers, and a Builder-style API. It is our default for client-side SDKs we ship to customers, and for any short-lived edge worker where bundle size matters more than feature depth. If you are pairing ky with a TypeScript codebase, the inference is excellent — better than axios's, on par with undici's.

A retry-with-jitter pattern that works in any client

retry.js
// Drop-in retry helper that works with fetch, undici.request, or any
// promise-returning function. No external deps, sandbox-safe.
export async function retry(fn, { attempts = 5, baseMs = 200, capMs = 4000 } = {}) {
  let last
  for (let i = 0; i < attempts; i++) {
    try { return await fn() }
    catch (err) {
      last = err
      if (i === attempts - 1) break
      const expo = Math.min(capMs, baseMs * 2 ** i)
      const jitter = Math.random() * expo
      await new Promise(r => setTimeout(r, jitter))
    }
  }
  throw last
}

// Usage with the built-in fetch
const res = await retry(() => fetch('https://api.flaky.dev/data', {
  signal: AbortSignal.timeout(2000)
}))
Figure 4 — Interactive radar: developer-experience score across five dimensions for undici, fetch, got, and axios.

A production checklist regardless of which client you pick

Always set explicit timeouts

AbortSignal.timeout(ms) is now the standard way to bound any HTTP call. Without an explicit timeout, a slow upstream will eat your event loop and cascade into thread exhaustion. Set both a per-request timeout and a global Agent-level timeout so a single bad origin cannot starve the rest of your traffic.

Pool connections, do not open and close

Every TLS handshake costs ~30–100ms. If you are calling the same origin more than once per second, you should be reusing connections. undici Agents pool by default; the built-in fetch inherits that pool when you call setGlobalDispatcher. For more on tuning Node backend services at scale, our team has shipped this pattern across dozens of high-throughput APIs.

Wire in OpenTelemetry early

Every modern HTTP client supports OTel auto-instrumentation. Wire it in on day one so you can trace which upstream is slow before users start complaining. undici emits diagnostics_channel events that drop straight into traces; fetch is instrumented via @opentelemetry/instrumentation-undici because they share the engine.

💡Tip
Audit your top 10 outbound HTTP calls quarterly. The mix of upstreams in any non-trivial backend drifts every quarter — a vendor you barely used last year is suddenly on the critical path. A 30-minute audit of latency and error rate per upstream usually surfaces one or two wins.

Hire Expert Node.js Developers — Ready in 48 Hours

Picking the right HTTP client is one of dozens of small calls that compound into a fast, reliable Node.js backend. The engineers who make these calls correctly the first time save you months of refactoring. HireNodeJS.com specialises exclusively in Node.js talent: every developer is pre-vetted on real-world projects covering API design, event-driven architecture, observability, and production deployments.

Unlike generalist platforms, our curated pool means you speak only to engineers who live and breathe Node.js. Most clients have their first developer working within 48 hours of getting in touch. If you are also building a Node.js + GraphQL layer or a Redis-backed rate-limit tier, we have specialists for each. Engagements start as short-term contracts and can convert to full-time hires with zero placement fee.

💡Tip
🚀 Ready to scale your Node.js team? HireNodeJS.com connects you with pre-vetted engineers who can join within 48 hours — no lengthy screening, no recruiter fees. Browse developers at hirenodejs.com/hire

Conclusion: pick by constraint, not by habit

In 2026, the right Node.js HTTP client depends on the constraint you are optimising for. If you need maximum throughput and live entirely inside a Node process, undici wins by a wide margin. If you need cross-runtime portability — Node, Bun, Workers, Lambda Edge — the built-in fetch is the only correct answer. If you have an existing axios codebase with rich interceptors, keep it and tune the underlying agent. If you need retries, streams, or pagination helpers as first-class features, reach for got. If you ship a small SDK or run code at the edge, ky's 5KB footprint pays back every cold start.

Whichever client you choose, the production checklist is the same: set explicit timeouts, pool connections, instrument with OpenTelemetry, and re-audit your top upstreams quarterly. Get those four right and the client you picked matters far less than the discipline around it.

Topics
#nodejs#http-clients#undici#axios#fetch#performance#benchmarks

Frequently Asked Questions

What is the fastest Node.js HTTP client in 2026?

undici is the fastest, reaching ~82,000 requests per second on a 500-VU k6 benchmark — roughly 2× the throughput of axios on the same workload. The built-in global fetch is a close second because it uses undici under the hood.

Should I still use axios in 2026?

Yes, if you have an existing codebase with interceptors, defaults, or a large internal SDK that depends on axios's response shape. For greenfield projects, start with the built-in fetch or undici instead.

Is the built-in fetch in Node.js production-ready?

Yes. Node.js 22 LTS marks fetch as stable, and the implementation is backed by undici — the same library that wins our throughput benchmarks. The main limitation is that fetch does not expose Node-specific knobs like dispatcher tuning.

What is the difference between got and ky?

got (~200KB) is the heavyweight Node-only client with built-in retries, streams, hooks, and pagination. ky (~5KB) is a tiny fetch wrapper that adds retries and a cleaner API. Pick got for full-featured backend code, ky for SDKs and edge runtimes.

How do I add retries to fetch without external dependencies?

Wrap your fetch call in a loop with exponential backoff and jitter — typically 5 attempts, doubling delay from 200ms with random jitter, capped at 4 seconds. The retry helper in this article shows the full pattern in 12 lines.

Does undici support HTTP/2 and HTTP/3?

Yes for HTTP/2 (stable in undici 6+), experimental for HTTP/3. undici is the only Node.js HTTP client with native HTTP/2 multiplexing today, which is one reason it dominates throughput benchmarks against axios.

About the Author
Vivek Singh
Founder & CEO at Witarist

Vivek Singh is the founder of Witarist and HireNodeJS.com — a platform connecting companies with pre-vetted Node.js developers. With years of experience scaling engineering teams, Vivek shares insights on hiring, tech talent, and building with Node.js.

Developers available now

Want a Node.js engineer who ships fast, optimised APIs?

HireNodeJS connects you with pre-vetted senior Node.js engineers who know how to pick the right HTTP client, tune dispatchers, and instrument production traffic. Available within 48 hours, no recruiter fees.