mrkeyoor.com_
Tue 22 Sept 18:48 UTC
npmWeb Frontendupdated 22 Sept 2026

ky review

Ky 2.0.2 wraps the Fetch API with JSON request shortcuts, automatic errors for non-2xx responses, retries, per-attempt and total timeouts, lifecycle hooks, configured clients, progress callbacks, and Standard Schema response validation. It still returns web `Response` objects, so existing knowledge of fetch, headers, abort signals, and body consumption applies. The 2.0.2 patch fixes deletions and mutation leakage when `searchParams` are changed by an init hook. In our browser bundle test it added 20.5 KB minified and 7.3 KB gzipped, with no package dependencies.

Verdict

Ky earns its place when a Node 22 or modern-browser project needs one fetch-shaped policy for retries, hooks, JSON, and deadlines. Stay with native fetch for simple calls, and disable Ky retries for large streaming bodies.

We installed it

Lab card: what happened when we installed kyScreenshot of ky documentation
Install✓ · 0.3s1 package on disk · 1 MB
ImportESM import works · require() works · ESM package with exports map
Browser7.3 KBgzipped (20.5 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does ky install cleanly?

Yes. In a fresh container with an empty cache, npm install ky finished in 0.3s, leaving 1 package and 1 MB on disk. npm audit reported no known vulnerabilities.

How much does ky add to a browser bundle?

7.3 KB gzipped (20.5 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does ky work with both ESM and CommonJS?

Yes. Both import 'ky' and require('ky') worked in Node 22 in our run. The package is published as ESM with an exports map.

Does ky include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

ky or axios: which should you use?

axios: Use it for projects needing its interceptor model, wider historical runtime coverage, adapters, and a larger integration catalog. Ky earns its place when a Node 22 or modern-browser project needs one fetch-shaped policy for retries, hooks, JSON, and deadlines.

When should you not use ky?

The application makes a few direct requests with no shared policy. Native fetch already ships in every runtime targeted by Ky 2 and avoids another behavior layer.

API stability3/5The package keeps standard Request, Response, Headers, AbortSignal, and fetch options at its center, so most calls remain recognizable. Ky 2 is still a meaningful migration: it requires Node 22, renames `prefixUrl` to `prefix`, changes every hook to a state object, merges search parameters, throws for empty JSON, and moves parsed HTTP error bodies to `error.data`. Patches 2.0.1 and 2.0.2 then corrected fetch option forwarding and init-hook search parameter mutation, evidence that the new major settled through follow-up fixes.
Docs4/5The versioned README lists option types and defaults, retry status codes and delays, hook order and return rules, error classes, timeout scope, URL resolution, schema validation, progress support, streaming costs, SSR, and testing. Security warnings cover credentials on replacement requests. Navigation is still a single long GitHub document, and even the v2.0.2 tag includes a notice that the README is for the next version, forcing readers to cross-check the npm package or release notes before adopting a newer example.
Maintenance5/5npm published 2.0.2 on 2026-04-21, and GitHub records a push on 2026-07-06. The repository is unarchived, has 17,042 stars, and reports only 3 open issues and pull requests. Version 2.0.1 repaired custom-fetch compatibility and option forwarding; 2.0.2 repaired init-hook query deletion and mutation isolation. Zero runtime dependencies reduce the transitive update surface, while the Node 22 floor lets maintenance focus on current Fetch implementations.
Ecosystem5/5The npm downloads endpoint counted 6,940,365 installs for the latest completed week. Ky runs on modern browsers, Node 22, Bun, and Deno by building on shared Fetch API objects, and it accepts a custom fetch implementation for framework or instrumentation needs. Standard Schema support connects JSON parsing to validators such as compatible Zod versions. Its extension story is mostly configured instances and hooks rather than adapters, which suits web-standard runtimes but offers less for older or Node-specific transports.

Use it if

  • Your browser or Node 22 application already uses fetch concepts but repeats status checks, JSON headers, retries, and timeout wiring.
  • Several API clients need separate base URLs, headers, hook chains, and retry policies while retaining standard Request and Response objects.
  • TypeScript should treat unannotated JSON as `unknown`, with optional runtime validation through a Standard Schema implementation.
  • A request layer needs bounded retries plus separate per-attempt and whole-operation deadlines.
Skip it if

Setup reality

We installed Ky 2.0.2 in a clean Node 22 Bookworm container. npm completed in 0.3 seconds and left one package using 1 MB. Ky has zero direct dependencies and zero peer dependencies; npm reports 668 KB unpacked. The package includes TypeScript declarations and uses ESM with an exports map. Both require() and ESM import worked under Node 22.23.2. npm audit found zero known vulnerabilities at every severity.

Our esbuild browser check produced 20.5 KB minified and 7.3 KB gzipped. No credentials or config file are required, though browser CORS policy still applies. Server-side relative URLs need a usable baseUrl; a base containing a path should end with / or standard URL resolution can replace its final segment. prefix performs string joining and treats a leading slash differently, so choose it only when that behavior is wanted.

Ky retries selected idempotent methods and status codes with a default limit of two. The normal timeout applies to each attempt, while totalTimeout bounds attempts and delays together. Timeout failures are excluded from retries unless retryOnTimeout is enabled. Retried streams are split with tee() and buffered, which can turn a large upload into a memory problem. Set retry: {limit: 0} for a body that should not be replayed.

Version 2 gives hooks one state object such as {request, options, retryCount}. An init hook is synchronous; other hook arrays run serially. HTTPError.data contains the already parsed error body, while network and timeout errors have no response. A generic passed to .json<T>() changes TypeScript's belief only. Pass a Standard Schema validator when untrusted JSON needs a runtime check. The 2.0.2 patch specifically prevents init-hook search parameter edits from leaking across requests.

Patterns

Request a typed JSON value read-typed-json

import ky from 'ky';

type User = {id: string; name: string};
const user = await ky.get('https://api.example.com/users/42').json<User>();

The type argument performs no runtime validation. Without an argument or schema, Ky types the JSON result as `unknown`.

Create a record with JSON send-json-body

const created = await ky.post('https://api.example.com/users', {
  json: {name: 'Mina'},
}).json<{id: string}>();

The `json` option stringifies the value and sets the content type. The response shortcut throws on an empty body or status 204.

Set shared URL and deadlines create-api-client

const api = ky.create({
  baseUrl: 'https://api.example.com/v1/',
  timeout: 5_000,
  totalTimeout: 20_000,
  headers: {'X-Client': 'dashboard'},
});

const users = await api.get('users').json();

Keep the trailing slash on a base URL path. Without it, resolving `users` replaces the last path segment.

Add query values to an existing URL merge-search-parameters

const page = await ky.get('https://api.example.com/search?lang=en', {
  searchParams: {q: 'http clients', page: 2},
}).json();

Ky 2 merges these values with the input query. Version 2.0.2 fixes mutation leakage when init hooks edit tuple-form parameters.

Add a bearer token in a hook attach-authentication

const authed = api.extend({
  hooks: {
    beforeRequest: [({request}) => {
      const token = readToken();
      if (token) request.headers.set('Authorization', `Bearer ${token}`);
    }],
  },
});

Ky 2 hooks receive one state object. `beforeRequest` runs before retry handling; use `beforeRetry` when credentials must change for another attempt.

Add jitter and an overall deadline bound-retry-window

const report = await api.get('reports/latest', {
  retry: {
    limit: 3,
    jitter: true,
    retryOnTimeout: true,
  },
  timeout: 4_000,
  totalTimeout: 15_000,
}).json();

`timeout` applies per attempt; `totalTimeout` includes attempts and wait periods. Keep automatic retries to requests that are safe to repeat.

Send a stream without replay buffering disable-upload-retries

await ky.post('https://api.example.com/import', {
  body: readableStream,
  retry: {limit: 0},
  headers: {'content-type': 'application/octet-stream'},
});

Enabled retries tee and buffer a stream body. A zero limit avoids that memory cost and prevents a second upload attempt.

Read parsed error data inspect-http-failure

import {isHTTPError} from 'ky';

try {
  await api.get('users/missing').json();
} catch (error) {
  if (isHTTPError(error)) {
    console.error(error.response.status, error.data);
    return;
  }
  throw error;
}

Ky 2 already consumes the HTTP error body. Read `error.data`; network and timeout errors do not carry a response.

Return non-2xx responses keep-fetch-status-handling

const response = await ky.get('https://api.example.com/health', {
  throwHttpErrors: false,
});

if (!response.ok) console.error(response.status);

With this option, HTTP failures are returned and are not retried. Network failures and timeouts may still throw.

Abort work from the caller cancel-inflight-request

const controller = new AbortController();
const promise = api.get('reports/slow', {signal: controller.signal}).json();

controller.abort();
await promise;

The promise rejects after cancellation. Handle that rejection when aborting is part of normal UI behavior.

Check JSON against a schema validate-json-response

import {z} from 'zod';

const userSchema = z.object({id: z.string(), name: z.string()});
const user = await api.get('users/42').json(userSchema);

A Standard Schema mismatch throws `SchemaValidationError`; compatible Zod versions implement that contract.

Track a response body observe-download-progress

const archive = await ky.get('https://cdn.example.com/archive.zip', {
  onDownloadProgress(progress, chunk) {
    console.log(progress.transferredBytes, progress.totalBytes, chunk.byteLength);
  },
}).arrayBuffer();

`totalBytes` may be zero when the server provides no usable length. Ky reads the response body to deliver these callbacks.

Alternatives

PackageRegistryPick it when
axiosnpmUse it for projects needing its interceptor model, wider historical runtime coverage, adapters, and a larger integration catalog.
gotnpmUse it in Node-only services that need deeper stream, pagination, DNS, agent, and transport controls.
ofetchnpmUse it for a small fetch wrapper closely tied to Nuxt, Nitro, and universal server runtime conventions.

More web frontend guides

postcss · react · react-dom · tailwindcss · htmlparser2 · tailwind-merge · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.