🌳 Tiny & elegant JavaScript HTTP client based on the Fetch API

★ 17,149⑂ 512
TypeScriptCreated 2018-09-04
17,149
stars
+3+5+43
512
forks
–
watchers
–
prs
–
issues
–
releases
–
discussions

README

Ky is a tiny and elegant HTTP client based on the Fetch API




Ky targets modern browsers, Node.js, Bun, and Deno.

It's just a tiny package with no dependencies.

 Benefits over plain fetch

- Simpler API
- Method shortcuts (ky.post())
- Treats non-2xx status codes as errors (after redirects)
- Retries failed requests
- JSON option
- Timeout support
- Upload and download progress
- Base URL option
- Instances with custom defaults
- Hooks
- Response validation with Standard Schema (Zod, Valibot, etc.)
- TypeScript niceties (e.g., .json() supports generics and defaults to unknown, not any)

 Install

sh
npm install ky


 [!NOTE]
 This readme is for the next version of Ky. For the current version, click here.

 CDN

- jsdelivr
- unpkg
- esm.sh

 Usage

js
import ky from 'ky';

const json = await ky.post('https://example.com', {json: {foo: true}}).json();

console.log(json);
//= {data: '🦄'}


With plain fetch, it would be:

js
class HTTPError extends Error {}

const response = await fetch('https://example.com', {
	method: 'POST',
	body: JSON.stringify({foo: true}),
	headers: {
		'content-type': 'application/json'
	}
});

if (!response.ok) {
	throw new HTTPError(Fetch error: ${response.statusText});
}

const json = await response.json();

console.log(json);
//= {data: '🦄'}


If you are using Deno, import Ky from a URL. For example, using a CDN:

js
import ky from 'https://esm.sh/ky';


 API

 ky(input, options?)

The input and options are the same as fetch, with additional options available (see below).

Returns a Response object with Body methods added for convenience. So you can, for example, call ky.get(input).json() directly without having to await the Response first. When called like that, an appropriate Accept header will be set depending on the body method used. Unlike the Body methods of window.fetch, these will throw an HTTPError if the response status is not in the range of 200...299. Note that .json() will throw if the body is empty, as it cannot be parsed. However, you can use the parseJson option to add custom handling for empty bodies.

Available body shortcuts: .json(), .text(), .formData(), .arrayBuffer(), .blob(), and .bytes(). The .bytes() shortcut is only present when the runtime supports Response.prototype.bytes().

Invalid options, an invalid input, and an error thrown by an init hook reject the returned promise instead of throwing, the same way fetch() reports them. They are not passed to beforeError hooks, because no request was made.

js
import ky from 'ky';

const user = await ky('/api/user').json();

console.log(user);


⌨️ TypeScript: Accepts an optional type parameter, which defaults to unknown, and is passed through to the return type of .json().

ts
import ky from 'ky';

// user1 is unknown
const user1 = await ky('/api/users/1').json();
// user2 is a User
const user2 = await ky('/api/users/2').json();
// user3 is a User
const user3 = await ky('/api/users/3').json();

console.log([user1, user2, user3]);


You can also ge

Snapshot: 2026-11-10T09:35:25Z · Data license Apache-2.0 · JSON