MCPcopy
hub / github.com/sindresorhus/ky

github.com/sindresorhus/ky @v2.0.2 sqlite

repository ↗ · DeepWiki ↗ · release v2.0.2 ↗
178 symbols 441 edges 52 files 0 documented · 0%
README
    <img width="600" height="600" src="https://github.com/sindresorhus/ky/raw/v2.0.2/media/logo.svg" alt="ky">















        <sup>
            Sindre's open source work is supported by the community.

Special thanks to:

    <a href="https://circleback.ai?utm_source=sindresorhus&utm_medium=sponsorship&utm_campaign=awesome-list&utm_id=ky">



            <img width="280" src="https://sindresorhus.com/assets/thanks/circleback-logo.png?x" alt="Circleback logo">



        <b>Get the most out of every conversation.</b>



            <sup>AI-powered meeting notes, automations, and search. Give AI agents the context they need to get things done.</sup>



    </a>

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

Coverage Status

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

npm install ky

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

CDN

Usage

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:

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:

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. Also, .json() throws if the body is empty or the response status is 204.

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

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().

import ky from 'ky';

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

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

You can also get the response body as JSON and validate it with a Standard Schema compatible validator (for example, Zod 3.24+). This throws a SchemaValidationError when validation fails.

import ky, {SchemaValidationError} from 'ky';
import {z} from 'zod';

const userSchema = z.object({name: z.string()});

try {
    const user = await ky('/api/user').json(userSchema);
    console.log(user.name);
} catch (error) {
    if (error instanceof SchemaValidationError) {
        console.error(error.issues);
    }
}
// Get raw bytes (when supported by the runtime)
const bytes = await ky('/api/file').bytes();
console.log(bytes instanceof Uint8Array);

ky.get(input, options?)

ky.post(input, options?)

ky.put(input, options?)

ky.patch(input, options?)

ky.head(input, options?)

ky.delete(input, options?)

Sets options.method to the method name and makes a request.

⌨️ TypeScript: Accepts an optional type parameter for use with JSON responses (see ky()).

input

Type: string | URL | Request

Same as fetch input.

When using a Request instance as input, any URL altering options (such as baseUrl) will be ignored.

options

Type: object

Same as fetch options, plus the following additional options:

method

Type: string\ Default: 'get'

HTTP method used to make the request.

Internally, the standard methods (GET, POST, PUT, PATCH, HEAD and DELETE) are uppercased in order to avoid server errors due to case sensitivity.

json

Type: object and any other value accepted by JSON.stringify()

Shortcut for sending JSON. Use this instead of the body option. Accepts any plain object or value, which will be stringified using JSON.stringify() and sent in the body with the correct header set.

searchParams

Type: string | object<string, string | number | boolean | undefined> | Array<Array<string | number | boolean>> | URLSearchParams\ Default: ''

Search parameters to include in the request URL. Setting this will merge with any existing search parameters in the input URL.

Accepts any value supported by URLSearchParams().

When passing an object, setting a value to undefined deletes the parameter, while null values are preserved and converted to the string 'null'.

baseUrl

Type: string | URL

A base URL to resolve the input against. When the input (after applying the prefix option) is only a relative URL, such as 'users', '/users', or '//my-site.com', it will be resolved against the baseUrl to determine the destination of the request. Otherwise, the input is absolute, such as 'https://my-site.com', and it will bypass the baseUrl.

Useful when used with ky.extend() to create niche-specific Ky instances.

If the baseUrl itself is relative, it will be resolved against the environment's base URL, such as document.baseURI in browsers or location.href in Deno (see the --location flag).

Tip: When setting a baseUrl that has a path, we recommend that it include a trailing slash /, as in '/api/' rather than /api. This ensures more intuitive behavior for page-relative input URLs, such as 'users' or './users', where they will extend from the full path of the baseUrl rather than replacing its last path segment.

import ky from 'ky';

// On https://example.com

const response = await ky('users', {baseUrl: '/api/'});
//=> 'https://example.com/api/users'

const response = await ky('/users', {baseUrl: '/api/'});
//=> 'https://example.com/users'

See FAQ: baseUrl vs prefix

prefix

Type: string | URL

A prefix to prepend to the input before making the request (and before it is resolved against the baseUrl). It can be any valid path or URL, either relative or absolute. A trailing slash / is optional and will be added automatically, if needed, when it is joined with input. Only takes effect when input is a string.

Useful when used with ky.extend() to create niche-specific Ky instances.

In most cases, you should use the baseUrl option instead, as it is more consistent with web standards. However, prefix is useful if you want origin-relative input URLs, such as /users, to be treated as if they were page-relative. In other words, the leading slash of the input will essentially be ignored, because the prefix will become part of the input before URL resolution happens.

See FAQ: baseUrl vs prefix

import ky from 'ky';

// On https://example.com

const response = await ky('users', {prefix: '/api/'});
//=> 'https://example.com/api/users'

const response = await ky('/users', {prefix: '/api/'});
//=> 'https://example.com/api/users'

Notes: - The prefix and input are joined with a slash /, and slashes are normalized at the join boundary by trimming trailing slashes from prefix and leading slashes from input. - After prefix and input are joined, the result is resolved against the baseUrl option, if present.

retry

Type: object | number\ Default: - limit: 2 - methods: get put head delete options trace - statusCodes: 408 413 429 500 502 503 504 - afterStatusCodes: 413, 429, 503 - maxRetryAfter: Infinity - backoffLimit: Infinity - delay: attemptCount => 0.3 * (2 ** (attemptCount - 1)) * 1000 - jitter: undefined - retryOnTimeout: false - shouldRetry: undefined

Controls retry behavior. Each field is documented individually below.

If retry is a number, it will be used as limit and other defaults will remain in place.

Network errors (e.g., DNS failures, connection refused, offline) are automatically retried for retriable methods. Only errors recognized as network errors are retried; other errors (e.g., programming bugs) are thrown immediately. Use shouldRetry to customize this behavior.

If the response provides an HTTP status contained in afterStatusCodes, Ky will wait until the date, timeout, or timestamp given in the Retry-After header has passed to retry the request. If Retry-After is missing, the non-standard RateLimit-Reset header is used in its place as a fallback. If the provided status code is not in the list, the Retry-After header will be ignored.

If Retry-After header is greater than maxRetryAfter, it will use maxRetryAfter.

The backoffLimit option is the upper limit of the delay per retry in milliseconds. To clamp the delay, set backoffLimit to 1000, for example. By default, the delay is calculated with 0.3 * (2 ** (attemptCount - 1)) * 1000. The delay increases exponentially.

The delay option can be used to change how the delay between retries is calculated. The function receives one parameter, the attempt count, starting at 1, and must return the delay in milliseconds.

The jitter option adds random jitter to retry delays to prevent thundering herd problems. When many clients retry simultaneously (e.g., after hitting a rate limit), they can overwhelm the server again. Jitter adds randomness to break this synchronization. Set to true to use full jitter, which randomizes the delay between 0 and the computed delay. Alternatively, pass a function to implement custom jitter strategies.

Note: Jitter is not applied when the server provides a Retry-After header, as the server's explicit timing should be respected.

The retryOnTimeout option determines whether to retry when a request times out. By default, retries are not triggered following a timeout.

The shouldRetry option provides custom retry logic that takes precedence over the default retry checks (retryOnTimeout, status code checks, etc.) for retriable methods. It is only called after the retry limit and method checks pass.

Note: This is different from the beforeRetry hook: - shouldRetry: Controls WHETHER to retry (called before the retry decision is made) - beforeRetry: Called AFTER retry is confirmed, allowing y

Extension points exported contracts — how you extend this code

Options (Interface)
(no doc)
source/types/options.ts
Window (Interface)
(no doc)
test/browser.ts
NormalizedOptions (Interface)
(no doc)
source/types/options.ts

Core symbols most depended-on inside this repo

createHttpTestServer
called by 316
test/helpers/create-http-test-server.ts
ky
called by 229
source/index.ts
create
called by 51
source/core/Ky.ts
delay
called by 31
source/utils/delay.ts
getBodySize
called by 25
source/utils/body.ts
replaceOption
called by 21
source/utils/merge.ts
isHTTPError
called by 19
source/utils/type-guards.ts
addKyScriptToPage
called by 16
test/browser.ts

Shape

Function 112
Method 39
Class 24
Interface 3

Languages

TypeScript100%

Modules by API surface

source/core/Ky.ts38 symbols
test/hooks.ts27 symbols
source/utils/merge.ts12 symbols
test/main.ts9 symbols
test/retry.ts7 symbols
test/http-error.ts6 symbols
source/utils/type-guards.ts6 symbols
source/utils/body.ts6 symbols
source/core/constants.ts6 symbols
test/headers.ts5 symbols
test/browser.ts5 symbols
test/stream.ts4 symbols

Dependencies from manifests, versioned

@sindresorhus/tsconfig8.1.0 · 1×
@type-challenges/utils0.1.1 · 1×
@types/busboy1.5.4 · 1×
@types/express5.0.6 · 1×
@types/node25.3.0 · 1×
ava6.4.1 · 1×
busboy1.6.0 · 1×
del-cli7.0.0 · 1×
expect-type1.3.0 · 1×
express5.2.1 · 1×
jest-leak-detector30.2.0 · 1×
playwright1.58.2 · 1×

For agents

$ claude mcp add ky \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact