# Technical Documentation: sindresorhus/got

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/sindresorhus/got)

### Overview (`readme.md`)

<div align="center">
	<br>
	<br>
	<img width="360" src="media/logo.svg" alt="Got">
	<br>
	<br>
	<br>
	<br>
	<hr>
	<p>
		<p>
			<sup>
				Sindre's open source work is supported by the community.<br>Special thanks to:
			</sup>
		</p>
		<br>
		<br>
		<a href="https://www.fame.fi#gh-light-mode-only">
			<img src="https://sindresorhus.com/assets/thanks/fame-logo-light.svg" width="200" alt="Fame Helsinki">
		</a>
		<a href="https://www.fame.fi#gh-dark-mode-only">
			<img src="https://sindresorhus.com/assets/thanks/fame-logo-dark.svg" width="200" alt="Fame Helsinki">
		</a>
		<br>
		<br>
		<br>
		<br>
		<a href="https://depot.dev?utm_source=github&utm_medium=sindresorhus">
			<div>
				<picture>
					<source width="180" media="(prefers-color-scheme: dark)" srcset="https://sindresorhus.com/assets/thanks/depot-logo-dark.svg">
					<source width="180" media="(prefers-color-scheme: light)" srcset="https://sindresorhus.com/assets/thanks/depot-logo-light.svg">
					<img width="180" src="https://sindresorhus.com/assets/thanks/depot-logo-light.svg" alt="Depot logo">
				</picture>
			</div>
			<b>Fast remote container builds and GitHub Actions runners.</b>
		</a>
		<br>
		<br>
		<br>
	</p>
	<hr>
	<br>
	<br>
	<br>
	<br>
	<br>
	<br>
	<br>
</div>

> Human-friendly and powerful HTTP request library for Node.js


[](https://npmjs.com/got)
[](https://packagephobia.com/result?p=got)

[See how Got compares to other HTTP libraries](#comparison)

---

**You probably want [Ky](https://github.com/sindresorhus/ky) instead, by the same people. It's smaller, works in the browser too, and is more stable since it's built on [`Fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). Or [fetch-extras](https://github.com/sindresorhus/fetch-extras) for simple needs.**

---

**Support questions should be asked [here](https://github.com/sindresorhus/got/discussions).**

## Install

```sh
npm install got
```

**Warning:** This package is native [ESM](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) and no longer provides a CommonJS export. If your project uses CommonJS, you will have to [convert to ESM](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c). Please don't open issues for questions regarding CommonJS / ESM.

**Got v11 is no longer maintained and we will not accept any backport requests.**

## Take a peek

**A [quick start](documentation/quick-start.md) guide is available.**

### JSON mode

Got has a dedicated option for handling JSON payload.\
Furthermore, the promise exposes a `.json<T>()` function that returns `Promise<T>`.

```js
import got from 'got';

const {data} = await got.post('https://httpbin.org/anything', {
	json: {
		hello: 'world'
	}
}).json();

console.log(data);
//=> {"hello": "world"}
```

For advanced JSON usage, check out the [`parseJson`](documentation/2-options.md#parsejson) and [`stringifyJson`](documentation/2-options.md#stringifyjson) options.

**For more useful tips like this, visit the [Tips](documentation/tips.md) page.**

## Highlights

- [Used by 10K+ packages and 5M+ repos](https://github.com/sindresorhus/got/network/dependents)
- [Actively maintained](https://github.com/sindresorhus/got/graphs/contributors)

## Documentation

By default, Got will retry on failure. To disable this option, set [`options.retry.limit`](documentation/7-retry.md#retry) to 0.

#### Main API

- [x] [Promise API](documentation/1-promise.md)
- [x] [Options](documentation/2-options.md)
- [x] [Stream API](documentation/3-streams.md)
- [x] [Pagination API](documentation/4-pagination.md)
- [x] [Advanced HTTPS API](documentation/5-https.md)
- [x] [HTTP/2 support](documentation/2-options.md#http2)
- [x] [`Response` class](documentation/3-streams.md#response-2)

#### Timeouts and retries

- [x] [Advanced timeout handling](documentation/6-timeout.md)
- [x] [Retries on failure](documentation/7-retry.md)
- [x] [Errors with metadata](documentation/8-errors.md)

#### Advanced creation

- [x] [Hooks](documentation/9-hooks.md)
- [x] [Instances](documentation/10-instances.md)
- [x] [Progress events & other events](documentation/3-streams.md#events)
- [x] [Plugins](documentation/lets-make-a-plugin.md)
- [x] [Compose](documentation/examples/advanced-creation.js)

#### Cache, Proxy and UNIX sockets

- [x] [RFC 7234 compliant caching](documentation/cache.md)
- [x] [Proxy support](documentation/tips.md#proxying)
- [x] [Unix Domain Sockets](documentation/2-options.md#enableunixsockets)

#### Integration

- [x] [Diagnostics Channel](documentation/diagnostics-channel.md)
- [x] [TypeScript support](documentation/typescript.md)
- [x] [AWS](documentation/tips.md#aws)
- [x] [Testing](documentation/tips.md#testing)

---

### Migration guides

- [Request migration guide](documentation/migration-guides/request.md)
  - [*(Note that Request is unmaintained)*](https://github.com/request/request/issues/3142)
- [Axios](documentation/migration-guides/axios.md)
- [Node.js](documentation/migration-guides/nodejs.md)

## Got plugins

- [`got4aws`](https://github.com/SamVerschueren/got4aws) - Got convenience wrapper to interact with AWS v4 signed APIs
- [`gh-got`](https://github.com/sindresorhus/gh-got) - Got convenience wrapper to interact with the GitHub API
- [`gl-got`](https://github.com/singapore/gl-got) - Got convenience wrapper to interact with the GitLab API
- [`gotql`](https://github.com/khaosdoctor/gotql) - Got convenience wrapper to interact with GraphQL using JSON-parsed queries instead of strings
- [`got-fetch`](https://github.com/alexghr/got-fetch) - Got with a [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) interface
- [`got-scraping`](https://github.com/apify/got-scraping) - Got wrapper specifically designed for web scraping purposes
- [`got-ssrf`](https://github.com/JaneJeon/got-ssrf) - Got wrapper to protect server-side requests against SSRF attacks

## Comparison

|                       | `got`               | [`node-fetch`][n0]   | [`ky`][k0]               | [`axios`][a0]      | [`superagent`][s0]     |
|-----------------------|:-------------------:|:--------------------:|:------------------------:|:------------------:|:----------------------:|
| HTTP/2 support        | :heavy_check_mark:  | :x:                  | :heavy_check_mark:       | :x:                | :heavy_check_mark:\*\* |
| Browser support       | :x:                 | :heavy_check_mark:\* | :heavy_check_mark:       | :heavy_check_mark: | :heavy_check_mark:     |
| Promise API           | :heavy_check_mark:  | :heavy_check_mark:   | :heavy_check_mark:       | :heavy_check_mark: | :heavy_check_mark:     |
| Stream API            | :heavy_check_mark:  | Node.js only         | :x:                      | :x:                | :heavy_check_mark:     |
| Pagination API        | :heavy_check_mark:  | :x:                  | :x:                      | :x:                | :x:                    |
| Request aborting       | :heavy_check_mark:  | :heavy_check_mark:   | :heavy_check_mark:       | :heavy_check_mark: | :heavy_check_mark:     |
| RFC 7234 caching      | :heavy_check_mark:  | :x:                  | :x:                      | :x:                | :x:                    |
| Cookies (out-of-the-box) | :heavy_check_mark: | :x:                | :x:                      | :x:                | :x:                    |
| Follows redirects     | :heavy_check_mark:  | :heavy_check_mark:   | :heavy_check_mark:       | :heavy_check_mark: | :heavy_check_mark:     |
| Retries on failure    | :heavy_check_mark:  | :x:                  | :heavy_check_mark:       | :x:                | :heavy_check_mark:     |
| Progress events       | :heavy_check_mark:  | :x:                  | :heavy_check_mark: | Browser only       | :heavy_check_mark:     |
| Handles gzip/deflate  | :heavy_check_mark:  | :heavy_check_mark:   | :heavy_check_mark:       | :heavy_check_mark: | :heavy_check_mark:     |
| Advanced timeouts     | :heavy_check_mark:  | :x:                  | :x:                      | :x:                | :x:                    |
| Timings               | :heavy_check_mark:  | :x:                  | :x:                      | :x:                | :x:                    |
| Errors with metadata  | :heavy_check_mark:  | :x:                  | :heavy_check_mark:       | :heavy_check_mark: | :x:                    |
| JSON mode             | :heavy_check_mark:  | :heavy_check_mark:   | :heavy_check_mark:       | :heavy_check_mark: | :heavy_check_mark:     |
| Custom defaults       | :heavy_check_mark:  | :x:                  | :heavy_check_mark:       | :heavy_check_mark: | :x:                    |
| Composable            | :heavy_check_mark:  | :x:                  | :x:                      | :x:                | :heavy_check_mark:     |
| Hooks                 | :heavy_check_mark:  | :x:                  | :heavy_check_mark:       | :heavy_check_mark: | :x:                    |
| Issues open           | [![][gio]][g1]      | [![][nio]][n1]       | [![][kio]][k1]           | [![][aio]][a1]     | [![][sio]][s1]         |
| Issues closed         | [![][gic]][g2]      | [![][nic]][n2]       | [![][kic]][k2]           | [![][aic]][a2]     | [![][sic]][s2]         |
| Downloads             | [![][gd]][g3]       | [![][nd]][n3]        | [![][kd]][k3]            | [![][ad]][a3]      | [![][sd]][s3]          |
| Coverage              | TBD                 | [![][nc]][n4]        | [![][kc]][k4]            | [![][ac]][a4]      | [![][sc]][s4]          |
| Build                 | [![][gb]][g5]       | [![][nb]][n5]        | [![][kb]][k5]            | [![][ab]][a5]      | [![][sb]][s5]          |
| Bugs                  | [![][gbg]][g6]      | [![][nbg]][n6]       | [![][kbg]][k6]           | [![][abg]][a6]     | [![][sbg]][s6]         |
| Dependents            | [![][gdp]][g7]      | [![][ndp]][n7]       | [![][kdp]][k7]           | [![][adp]][a7]     | [![][sdp]][s7]         |
| Install size          | [![][gis]][g8]      | [![][nis]][n8]       | [![][kis]][k8]           | [![][ais]][a8]     | [![][sis]][s8]         |
| GitHub stars          | [![][gs]][g9]       | [![][ns]][n9]        | [![][ks]][k9]            | [![][as]][a9]      | [![][ss]][s9]          |
| TypeScript support    | [![][gts]][g10]     | [![][nts]][n10]      | [![][kts]][k10]          | [![][ats]][a10]    | [![][sts]][s11]        |
| Last commit           | [![][glc]][g11]     | [![][nlc]][n11]      | [![][klc]][k11]          | [![][alc]][a11]    | [![][slc]][s11]        |

\* It's almost API compatible with the browser `fetch` API.\
\*\* Need to switch the protocol manually. Doesn't accept PUSH streams and doesn't reuse HTTP/2 sessions.\
:sparkle: Almost-stable feature, but the API may change. Don't hesitate to try it out!\
:grey_question: Feature in early stage of development. Very experimental.


[k0]: https://github.com/sindresorhus/ky
[n0]: https://github.com/node-fetch/node-fetch
[a0]: https://github.com/axios/axios
[s0]: https://github.com/visionmedia/superagent


[gio]: https://img.shields.io/github/issues-raw/sindresorhus/got?color=gray&label
[kio]: https://img.shields.io/github/issues-raw/sindresorhus/ky?color=gray&label
[nio]: https://img.shields.io/github/issues-raw/bitinn/node-fetch?color=gray&label
[aio]: https://img.shields.io/github/issues-raw/axios/axios?color=gray&label
[sio]: https://img.shields.io/github/issues-raw/visionmedia/superagent?color=gray&label

[g1]: https://github.com/sindresorhus/got/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc
[k1]: https://github.com/sindresorhus/ky/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc
[n1]: https://github.com/bitinn/node-fetch/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc
[a1]: https://github.com/axios/axios/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc
[s1]: https://github.com/visionmedia/superagent/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc


[gic]: https://img.shields.io/github/issues-closed-raw/sindresorhus/got?color=blue&label
[kic]: https://img.shields.io/github/issues-closed-raw/sindresorhus/ky?color=blue&label
[nic]: https://img.shields.io/github/issues-closed-raw/bitinn/node-fetch?color=blue&label
[aic]: https://img.shields.io/github/issues-closed-raw/axios/axios?color=blue&label
[sic]: https://img.shields.io/github/issues-closed-raw/visionmedia/superagent?color=blue&label

[g2]: https://github.com/sindresorhus/got/issues?q=is%3Aissue+is%3Aclosed+sort%3Aupdated-desc
[k2]: https://github.com/sindresorhus/ky/issues?q=is%3Aissue+is%3Aclosed+sort%3Aupdated-desc
[n2]: https://github.com/bitinn/node-fetch/issues?q=is%3Aissue+is%3Aclosed+sort%3Aupdated-desc
[a2]: https://github.com/axios/axios/issues?q=is%3Aissue+is%3Aclosed+sort%3Aupdated-desc
[s2]: https://github.com/visionmedia/superagent/issues?q=is%3Aissue+is%3Aclosed+sort%3Aupdated-desc


[gd]: https://img.shields.io/npm/dm/got?color=darkgreen&label
[kd]: https://img.shields.io/npm/dm/ky?color=darkgreen&label
[nd]: https://img.shields.io/npm/dm/node-fetch?color=darkgreen&label
[ad]: https://img.shields.io/npm/dm/axios?color=darkgreen&label
[sd]: https://img.shields.io/npm/dm/superagent?color=darkgreen&label

[g3]: https://www.npmjs.com/package/got
[k3]: https://www.npmjs.com/package/ky
[n3]: https://www.npmjs.com/package/node-fetch
[a3]: https://www.npmjs.com/package/axios
[s3]: https://www.npmjs.com/package/superagent


[kc]: https://img.shields.io/codecov/c/github/sindresorhus/ky?color=0b9062&label
[nc]: https://img.shields.io/coveralls/github/bitinn/node-fetch?color=0b9062&label
[ac]: https://img.shields.io/coveralls/github/mzabriskie/axios?color=0b9062&label
[sc]: https://img.shields.io/codecov/c/github/visionmedia/superagent?color=0b9062&label

[k4]: https://codecov.io/gh/sindresorhus/ky
[n4]: https://coveralls.io/github/bitinn/node-fetch
[a4]: https://coveralls.io/github/mzabriskie/axios
[s4]: https://codecov.io/gh/visionmedia/superagent


[gb]: https://github.com/sindresorhus/got/actions/workflows/main.yml/badge.svg
[kb]: https://github.com/sindresorhus/ky/actions/workflows/main.yml/badge.svg
[nb]: https://img.shields.io/travis/bitinn/node-fetch?label
[ab]: https://img.shields.io/travis/axios/axios?label
[sb]: https://img.shields.io/travis/visionmedia/superagent?label

[g5]: https://github.com/sindresorhus/got/actions/workflows/main.yml
[k5]: https://github.com/sindresorhus/ky/actions/workflows/main.yml
[n5]: https://travis-ci.org/github/bitinn/node-fetch
[a5]: https://travis-ci.org/github/axios/axios
[s5]: https://travis-ci.org/github/visionmedia/superagent


[gbg]: https://img.shields.io/github/issues-raw/sindresorhus/got/bug?color=darkred&label
[kbg]: https://img.shields.io/github/issues-raw/sindresorhus/ky/bug?color=darkred&label
[nbg]: https://img.shields.io/github/issues-raw/bitinn/node-fetch/bug?color=darkred&label
[abg]: https://img.shields.io/github/issues-raw/axios/axios/bug-fix?color=darkred&label
[sbg]: https://img.shields.io/github/issues-raw/visionmedia/superagent/Bug?color=darkred&label

[g6]: https://github.com/sindresorhus/got/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Abug
[k6]: https://github.com/sindresorhus/ky/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Abug
[n6]: https://github.com/bitinn/node-fetch/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Abug
[a6]: https://github.com/axios/axios/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22bug-fix%22
[s6]: https://github.com/visionmedia/superagent/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3ABug


[gdp]: https://badgen.net/npm/dependents/got?color=orange&label
[kdp]: https://badgen.net/npm/dependents/ky?color=orange&label
[ndp]: https://badgen.net/npm/dependents/node-fetch?color=orange&label
[adp]: https://badgen.net/npm/dependents/axios?color=orange&label
[sdp]: https://badgen.net/npm/dependents/superagent?color=orange&label

[g7]: https://www.npmjs.com/package/got?activeTab=dependents
[k7]: https://www.npmjs.com/package/ky?activeTab=dependents
[n7]: https://www.npmjs.com/package/node-fetch?activeTab=dependents
[a7]: https://www.npmjs.com/package/axios?activeTab=dependents
[s7]: https://www.npmjs.com/package/visionmedia?activeTab=dependents


[gis]: https://packagephobia.com/badge?p=got
[kis]: https://packagephobia.com/badge?p=ky
[nis]: https://packagephobia.com/badge?p=node-fetch
[ais]: https://packagephobia.com/badge?p=axios
[sis]: https://packagephobia.com/badge?p=superagent

[g8]: https://packagephobia.com/result?p=got
[k8]: https://packagephobia.com/result?p=ky
[n8]: https://packagephobia.com/result?p=node-fetch
[a8]: https://packagephobia.com/result?p=axios
[s8]: https://packagephobia.com/result?p=superagent


[gs]: https://img.shields.io/github/stars/sindresorhus/got?color=white&label
[ks]: https://img.shields.io/github/stars/sindresorhus/ky?color=white&label
[ns]: https://img.shields.io/github/stars/bitinn/node-fetch?color=white&label
[as]: https://img.shields.io/github/stars/axios/axios?color=white&label
[ss]: https://img.shields.io/github/stars/visionmedia/superagent?color=white&label

[g9]: https://github.com/sindresorhus/got
[k9]: https://github.com/sindresorhus/ky
[n9]: https://github.com/node-fetch/node-fetch
[a9]: https://github.com/axios/axios
[s9]: https://github.com/visionmedia/superagent


[gts]: https://badgen.net/npm/types/got?label
[kts]: https://badgen.net/npm/types/ky?label
[nts]: https://badgen.net/npm/types/node-fetch?label
[ats]: https://badgen.net/npm/types/axios?label
[sts]: https://badgen.net/npm/types/superagent?label

[g10]: https://github.com/sindresorhus/got
[k10]: https://github.com/sindresorhus/ky
[n10]: https://github.com/node-fetch/node-fetch
[a10]: https://github.com/axios/axios


[glc]: https://img.shields.io/github/last-commit/sindresorhus/got?color=gray&label
[klc]: https://img.shields.io/github/last-commit/sindresorhus/ky?color=gray&label
[nlc]: https://img.shields.io/github/last-commit/bitinn/node-fetch?color=gray&label
[alc]: https://img.shields.io/github/last-commit/axios/axios?color=gray&label
[slc]: https://img.shields.io/github/last-commit/visionmedia/superagent?color=gray&label

[g11]: https://github.com/sindresorhus/got/commits
[k11]: https://github.com/sindresorhus/ky/commits
[n11]: https://github.com/node-fetch/node-fetch/commits
[a11]: https://github.com/axios/axios/commits
[s11]: https://github.com/visionmedia/superagent/commits

[Click here][InstallSizeOfTheDependencies] to see the install size of the Got dependencies.

[InstallSizeOfTheDependencies]: https://packagephobia.com/result?p=@sindresorhus/is@8.0.0,byte-counter@0.1.0,cacheable-request@13.0.18,chunk-data@0.1.0,decompress-response@10.0.0,keyv@5.6.0,lowercase-keys@4.0.1,responselike@4.0.2,type-fest@5.6.0,uint8array-extras@1.5.0

## Maintainers

[](https://sindresorhus.com) | [](https://github.com/szmarczak)
---|---
[Sindre Sorhus](https://sindresorhus.com) | [Szymon Marczak](https://github.com/szmarczak)

---

### Documentation 1 Promise (`documentation/1-promise.md`)

[> Back to homepage](../readme.md#documentation)

## Promise API

Source code: [`source/as-promise/index.ts`](../source/as-promise/index.ts)

The main Got function returns a [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise).\
Request aborting is supported via the [`signal` option](2-options.md#signal) and [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController).

### <code>got(url: string | URL, options?: [OptionsInit](typescript.md#optionsinit), defaults?: [Options](2-options.md))</code>

**Returns: <code>Promise<[Response](response.md)></code>**

The most common way is to pass the URL as the first argument, then the options as the second.

```js
import got from 'got';

const {headers} = await got(
	'https://httpbin.org/anything',
	{
		headers: {
			foo: 'bar'
		}
	}
).json();
```

### `promise.json<T>()`

**Returns: `Promise<T>`**

A shortcut method that gives a Promise returning a JSON object.

It is semantically the same as setting [`options.resolveBodyOnly`](2-options.md#resolvebodyonly) to `true` and [`options.responseType`](2-options.md#responsetype) to `'json'`.

### `promise.buffer()`

**Returns: `Promise<Uint8Array>`**

A shortcut method that gives a Promise returning a [Uint8Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array).

It is semantically the same as setting [`options.resolveBodyOnly`](2-options.md#resolvebodyonly) to `true` and [`options.responseType`](2-options.md#responsetype) to `'buffer'`.

### `promise.text()`

**Returns: `Promise<string>`**

A shortcut method that gives a Promise returning a string.

It is semantically the same as setting [`options.resolveBodyOnly`](2-options.md#resolvebodyonly) to `true` and [`options.responseType`](2-options.md#responsetype) to `'text'`.

### `promise.on(event, handler)`

The events are the same as in [Stream API](3-streams.md#events).

The `response` event fires once, with the response the promise resolves with. It does not fire for a response that is retried, or for an `HTTPError` response when [`throwHttpErrors`](2-options.md#throwhttperrors) is `true`. A `response` listener that throws rejects the promise with that error.

### `promise.once(event, handler)`

Registers a one-time listener for events from [Stream API](3-streams.md#events).

### `promise.off(event, handler)`

Removes listener registered with [`promise.on`](1-promise.md#promiseonevent-handler).

```js
import {createReadStream} from 'node:fs';
import got from 'got';

const ongoingRequestPromise = got.post(uploadUrl, {
    body: createReadStream('sample.txt')
});

const eventListener = (progress: Progress) => {
    console.log(progress);
};

ongoingRequestPromise.on('uploadProgress', eventListener);

setTimeout(() => {
    ongoingRequestPromise.off('uploadProgress', eventListener);
}, 500);

await ongoingRequestPromise;
```

---

### Documentation 10 Instances (`documentation/10-instances.md`)

[> Back to homepage](../readme.md#documentation)

## Instances

Source code: [`source/create.ts`](../source/create.ts)

### `got.defaults`

#### `options`

**Type: [`Options`](2-options.md)**

The options used for this instance.

#### `handlers`

**Type: [`Handler[]`](typescript.md#handler)**

```ts
(options: Options, next: …) => next(options)
```

An array of handlers. The `next` function returns a [`Promise`](1-promise.md) or a [`Request` Got stream](3-streams.md).

You execute them directly by calling `got(…)`. They are some sort of "global hooks" - these functions are called first. The last handler (it's invisible) is either `asPromise` or `asStream`, depending on whether `got()` or `got.stream()` was called.

#### `mutableDefaults`

**Type: `boolean`**\
**Default: `false`**

Determines whether `got.defaults.options` can be modified.

> [!NOTE]
> Immutable defaults reject writes. Assigning to a frozen option throws, and reading `searchParams` returns a copy, so writing to that copy is discarded. Use `got.extend(…)` or the request itself to set `searchParams`.

### `got.extend(…options, …instances)`

**Tip:**
> - `options` can include `handlers` and `mutableDefaults`.

**Note:**
> - Properties that are not enumerable, such as `body`, `json`, and `form`, will not be merged. Pass them to each request instead of to `got.extend()`: an instance created with one keeps it in its recorded options, so `got.extend(instance, …)` can throw when another argument sets a different kind of payload.

Configure a new `got` instance with merged default options. The options are merged with the parent instance's `defaults.options` using [`options.merge(…)`](2-options.md#merge).

```js
import got from 'got';

const client = got.extend({
	prefixUrl: 'https://httpbin.org',
	headers: {
		'x-foo': 'bar'
	}
});

const {headers} = await client.get('headers').json();
console.log(headers['x-foo']); //=> 'bar'

const jsonClient = client.extend({
	responseType: 'json',
	resolveBodyOnly: true,
	headers: {
		'x-lorem': 'impsum'
	}
});

const {headers: headers2} = await jsonClient.get('headers');
console.log(headers2['x-foo']);   //=> 'bar'
console.log(headers2['x-lorem']); //=> 'impsum'
```

**Note:**
> - Handlers can be asynchronous and can return a `Promise`, but never a `Promise<Stream>` when `options.isStream` is `true` (set internally for `got.stream()` requests).
> - Streams must always be handled synchronously.
> - In order to perform async work using streams, the `beforeRequest` hook should be used instead.

The recommended approach for creating handlers that can handle both promises and streams is:

```js
import got from 'got';

// Create a non-async handler, but we can return a Promise later.
const handler = (options, next) => {
	// Internally set for requests created via `got.stream()`.
	if (options.isStream) {
		// It's a Stream, return synchronously.
		return next(options);
	}

	// For asynchronous work, return a Promise.
	return (async () => {
		try {
			const response = await next(options);
			response.yourOwnProperty = true;
			return response;
		} catch (error) {
			// Every error will be replaced by this one.
			// Before you receive any error here,
			// it will be passed to the `beforeError` hooks first.
			// Note: this one won't be passed to `beforeError` hook. It's final.
			throw new Error('Your very own error.');
		}
	})();
};

const instance = got.extend({handlers: [handler]});
```

---

### Documentation 2 Options (`documentation/2-options.md`)

[> Back to homepage](../readme.md#documentation)

## Options

Source code: [`source/core/options.ts`](../source/core/options.ts)

Like `fetch` stores the options in a `Request` instance, Got does so in `Options`.\
It is made of getters and setters that provide fast option normalization and validation.

**By default, Got will retry on failure. To disable this option, set [`options.retry`](7-retry.md) to `{limit: 0}`.**

#### Merge behavior explained

When an option is already set, setting it again replaces it with a deep clone by default.\
Otherwise the merge behavior is documented in the corresponding section for the option.

#### How to store options

The `Options` class is useful for storing and validating configuration for Got instances.

The constructor - `new Options(url, options, defaults)` - takes the same arguments as the `got` function.

To use an `Options` instance, create an extended Got instance:

```js
import got, {Options} from 'got';

const options = new Options({
	prefixUrl: 'https://httpbin.org',
	headers: {
		foo: 'foo'
	}
});

options.headers.foo = 'bar';

// Use got.extend() to create an instance with the Options
const instance = got.extend(options);

const {headers} = await instance('anything').json();
console.log(headers.foo);
//=> 'bar'
```

For most use cases, plain objects are simpler and more convenient:

```js
import got from 'got';

const options = {
	prefixUrl: 'https://httpbin.org',
	headers: {
		foo: 'foo'
	}
};

options.headers.foo = 'bar';

// Plain objects can be passed directly as the second argument
const {headers} = await got('anything', options).json();
console.log(headers.foo);
//=> 'bar'
```

Note that the `Options` constructor throws immediately when an invalid option is provided, such as a non-existing option or a typo. With plain objects, validation only happens when the request is made.

For TypeScript users, `got` exports a dedicated type called `OptionsInit`.\
It is a plain object that can store the same properties as `Options`.

The `Options` class is useful for storing the base configuration of a custom Got client, especially when you want early validation of options.

#### Resetting options

Resetting behavior depends on the option. Scalar options such as `responseType` and `resolveBodyOnly` ignore `undefined` and preserve the parent value. To restore their defaults, explicitly pass `responseType: 'text'` or `resolveBodyOnly: false`.

The following options support explicit resets:

```js
instance(…, {searchParams: undefined});
instance(…, {cookieJar: undefined});
instance(…, {responseType: 'text', resolveBodyOnly: false});
instance(…, {prefixUrl: ''});
instance(…, {agent: {http: undefined, https: undefined, http2: undefined}});
instance(…, {context: {token: undefined, …}});
instance(…, {https: {rejectUnauthorized: undefined, …}});
instance(…, {cacheOptions: {immutableMinTimeToLive: undefined, …}});
instance(…, {headers: {'user-agent': undefined, …}});
instance(…, {timeout: {request: undefined, …}});
```

In order to reset `hooks`, `retry` and `pagination`, another Got instance must be created:

```js
const defaults = new Options();

const secondInstance = instance.extend({mutableDefaults: true});
secondInstance.defaults.options.hooks = defaults.hooks;
secondInstance.defaults.options.retry = defaults.retry;
secondInstance.defaults.options.pagination = defaults.pagination;
```

### `url`

**Type: <code>string | [URL](https://nodejs.org/api/url.html#url_the_whatwg_url_api)</code>**

The URL to request. Usually the `url` represents a [WHATWG URL](https://url.spec.whatwg.org/#url-class).
Pass it as the first argument to `got(url, options)`. Passing `url` in an options object is not supported.

```js
import got from 'got';

// This:
await got('https://httpbin.org/anything');

// is semantically the same as this:
await got(new URL('https://httpbin.org/anything'));
```

> [!NOTE]
> Throws if no protocol specified.

> [!NOTE]
> If `url` is a string, then the `query` string will **not** be parsed as search params.\
>  This is in accordance to [the specification](https://datatracker.ietf.org/doc/html/rfc7230#section-2.7).\
>  If you want to pass search params instead, use the `searchParams` option below.

```js
import got from 'got';

await got('https://httpbin.org/anything?query=a b'); //=> ?query=a%20b
await got('https://httpbin.org/anything', {searchParams: {query: 'a b'}}); //=> ?query=a+b

// The query string is overridden by `searchParams`
await got('https://httpbin.org/anything?query=a b', {searchParams: {query: 'a b'}}); //=> ?query=a+b
```

> [!NOTE]
> Leading slashes are disallowed to enforce consistency and avoid confusion.\
>  For example, when the prefix URL is `https://example.com/foo` and the input is `/bar`, there's ambiguity whether the resulting URL would become `https://example.com/foo/bar` or `https://example.com/bar`. The latter is used by browsers.

### `searchParams`

**Type: <code>string | [URLSearchParams](https://nodejs.org/api/url.html#url_class_urlsearchparams) | object&lt;string, [Primitive](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html)&gt;</code>**

[WHATWG URL Search Params](https://url.spec.whatwg.org/#interface-urlsearchparams) to be added to the request URL.

```js
import got from 'got';

const response = await got('https://httpbin.org/anything', {
	searchParams: {
		hello: 'world',
		foo: 123
	}
}).json();

console.log(response.args);
//=> {hello: 'world', foo: 123}
```

If you need to pass an array, you can do it using a `URLSearchParams` instance:

```js
import got from 'got';

const searchParams = new URLSearchParams([['key', 'a'], ['key', 'b']]);

await got('https://httpbin.org/anything', {searchParams});

console.log(searchParams.toString());
//=> 'key=a&key=b'
```

> [!NOTE]
> This will override the `query` string in `url`.

> [!NOTE]
> - `null` values are not stringified, an empty string is used instead.
> - `undefined` values clear the keys inherited from the instance defaults. The `query` string in `url` is still overridden.

#### **Merge behavior:**
> - Overrides existing properties.

### `prefixUrl`

**Type: `string`**\
**Default: `''`**

The string to be prepended to relative string `url` input.

The prefix must be an [absolute URL](https://url.spec.whatwg.org/#absolute-url-string), since Got has no base to resolve it against. It must not contain a query string or a fragment.
A trailing slash `/` is optional - one will be added automatically.

```js
import got from 'got';

// This:
const instance = got.extend({prefixUrl: 'https://httpbin.org'});
await instance('anything');

// is semantically the same as this:
await got('https://httpbin.org/anything');
```

> [!NOTE]
> Changing `prefixUrl` also updates the `url` option if set.

> [!NOTE]
> Absolute string URLs and `URL` instances bypass `prefixUrl` by default. Other instance defaults, including `headers`, still apply. If those URLs may come from untrusted input, set [`allowAbsoluteUrls`](#allowabsoluteurls) to `false`.

> [!NOTE]
> Got cannot know which custom headers are sensitive. If you use headers like `x-api-key`, only pass trusted URLs or use `allowAbsoluteUrls: false`.

### `allowAbsoluteUrls`

**Type: `boolean`**\
**Default: `true`**

Allow absolute URLs to bypass `prefixUrl`.

When set to `false` with `prefixUrl`, passing an absolute URL will throw. This also rejects scheme-relative URL strings like `//example.com/path` in retry and pagination URL overrides. Use this when untrusted URL input must stay on the same origin as the configured `prefixUrl`. This is not a path sandbox: relative paths like `../other` still follow standard URL resolution on the same origin.

```js
import got from 'got';

const client = got.extend({
	prefixUrl: 'https://api.example.com',
	allowAbsoluteUrls: false,
	headers: {
		'x-api-key': process.env.API_KEY
	}
});

await client('users/1');
// Requests https://api.example.com/users/1

await client('https://attacker.example');
// Throws
```

Set `prefixUrl` to an empty string for a request that intentionally needs an absolute URL:

```js
await client('https://trusted.example', {prefixUrl: ''});
```

> [!NOTE]
> This guards the `url` you pass. It does not block cross-origin redirects issued by the server, though inherited sensitive headers are still stripped when a redirect changes origin.

> [!NOTE]
> The check is defeated if the same hook or `pagination.paginate(…)` return also sets `prefixUrl` or `allowAbsoluteUrls`. Do not populate those options from untrusted data.

### `signal`

**Type: [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)**

You can abort the `request` using [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController).

```js
import got from 'got';

const abortController = new AbortController();

const request = got('https://httpbin.org/anything', {
	signal: abortController.signal
});

setTimeout(() => {
	abortController.abort();
}, 100);
```

### `method`

**Type: `string`**\
**Default: `GET`**

The [HTTP method](https://httpwg.org/specs/rfc9110.html#methods) used to make the request.\
Common methods include: `GET`, `HEAD`, `POST`, `PUT`, `DELETE`.
Got also supports `QUERY`, which is defined in [RFC 10008](https://www.rfc-editor.org/rfc/rfc10008.html).
Custom methods such as `PROPFIND` are supported. Method names are normalized to uppercase.

```js
import got from 'got';

const {method} = await got('https://httpbin.org/anything', {
	method: 'POST'
}).json();

console.log(method);
// => 'POST'
```

### `headers`

**Type: `object<string, string | string[] | number>`**\
**Default: `{'user-agent': 'got (https://github.com/sindresorhus/got)'}`**

The [HTTP headers](https://datatracker.ietf.org/doc/html/rfc7231#section-8.3) to be sent. Headers set to `undefined` will be omitted. Number values are stored and sent as strings.

```js
import got from 'got';

const {headers} = await got.post('https://httpbin.org/anything', {
	headers: {
		hello: 'world'
	}
}).json();

console.log(headers);
// => {hello: 'world'}
```

#### **Merge behavior:**
> - Overrides existing properties.

### `body`

**Type: `string | ArrayBufferView | stream.Readable | Generator | AsyncGenerator | Iterable | AsyncIterable | FormData`**

The payload to send.

For `string` and `ArrayBufferView` types (including `Uint8Array`, other typed arrays, and `DataView`), the `content-length` header is automatically set if the `content-length` and `transfer-encoding` headers are missing.

**The `content-length` header is not automatically set when `body` is an instance of [`fs.createReadStream()`](https://nodejs.org/api/fs.html#fs_fs_createreadstream_path_options).**

To set `content-length` for file streams, you need to manually provide it using `fs.promises.stat()`:

```js
import fs from 'node:fs';
import fsPromises from 'node:fs/promises';
import got from 'got';

const filePath = 'path/to/file';
const fileStats = await fsPromises.stat(filePath);
const fileStream = fs.createReadStream(filePath);

await got.post('https://httpbin.org/anything', {
	body: fileStream,
	headers: {
		'content-length': fileStats.size.toString()
	}
});
```

```js
import got from 'got';

const {data} = await got.post('https://httpbin.org/anything', {
	body: 'Hello, world!'
}).json();

console.log(data);
//=> 'Hello, world!'
```

You can also use typed arrays (Uint8Array, Uint16Array, etc.) as request body:

```js
import got from 'got';

const uint8Body = new Uint8Array([104, 101, 108, 108, 111]); // 'hello' in ASCII

const {data} = await got.post('https://httpbin.org/anything', {
	body: uint8Body
}).json();

console.log(data);
//=> 'hello'
```

You can use `Iterable` and `AsyncIterable` objects as request body, including Web [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream):

```js
import got from 'got';

// Using an async generator
async function* generateData() {
	yield 'Hello, ';
	yield 'world!';
}

await got.post('https://httpbin.org/anything', {
	body: generateData()
});
```

You can use [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) objects as request body:

```js
import got from 'got';

const form = new FormData();
form.set('greeting', 'Hello, world!');

const data = await got.post('https://httpbin.org/post', {
	body: form
}).json();

console.log(data.form.greeting);
//=> 'Hello, world!'
```

> [!NOTE]
> If `body` is specified, then the `json` or `form` option cannot be used.

> [!NOTE]
> If you use this option, `got.stream()` will be read-only.

> [!NOTE]
> Passing `body` with `GET` will throw unless the [`allowGetBody` option](#allowgetbody) is set to `true`.

> [!NOTE]
> This option is not enumerable and will not be merged with the instance defaults.

### `json`

**Type: JSON-serializable values**

JSON **request** body. If set, the `content-type` header defaults to `application/json`.

> [!IMPORTANT]
> This option only affects the **request body** you send to the server. To parse the **response** as JSON, you must either call `.json()` on the promise or set [`responseType: 'json'`](#responsetype) in the options.

```js
import got from 'got';

const {data} = await got.post('https://httpbin.org/anything', {
	json: {
		hello: 'world'
	}
}).json();

console.log(data);
//=> `{hello: 'world'}`
```

### `form`

**Type: <code>object&lt;string, [Primitive](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html)&gt;</code>**

The form body is converted to a query string using `(new URLSearchParams(form)).toString()`.

If set, the `content-type` header defaults to [`application/x-www-form-urlencoded`](https://url.spec.whatwg.org/#application/x-www-form-urlencoded).

```js
import got from 'got';

const {data} = await got.post('https://httpbin.org/anything', {
	form: {
		hello: 'world'
	}
}).json();

console.log(data);
//=> 'hello=world'
```

### `parseJson`

**Type: `(text: string) => unknown`**\
**Default: `(text: string) => JSON.parse(text)`**

The function used to parse JSON responses.

```js
import got from 'got';
import {parse} from '@hapi/bourne';

// Preventing prototype pollution by using Bourne
const parsed = await got('https://example.com', {
	parseJson: text => parse(text)
}).json();

console.log(parsed);
```

### `stringifyJson`

**Type: `(object: unknown) => string`**\
**Default: `(object: unknown) => JSON.stringify(object)`**

The function used to stringify the body of JSON requests.

**Example: ignore all properties starting with an underscore**

```js
import got from 'got';

await got.post('https://example.com', {
	stringifyJson: object => JSON.stringify(object, (key, value) => {
		if (key.startsWith('_')) {
			return;
		}

		return value;
	}),
	json: {
		some: 'payload',
		_ignoreMe: 1234
	}
});
```

**Example: all numbers as strings**

```js
import got from 'got';

await got.post('https://example.com', {
	stringifyJson: object => JSON.stringify(object, (key, value) => {
		if (typeof value === 'number') {
			return value.toString();
		}

		return value;
	}),
	json: {
		some: 'payload',
		number: 1
	}
});
```

### `allowGetBody`

**Type: `boolean`**\
**Default: `false`**

Set this to `true` to allow sending body for the `GET` method.

> [!NOTE]
> This option is only meant to interact with non-compliant servers when you have no other choice.

> [!NOTE]
> The [RFC 7231](https://tools.ietf.org/html/rfc7231#section-4.3.1) doesn't specify any particular behavior for the GET method having a payload, therefore it's considered an [**anti-pattern**](https://en.wikipedia.org/wiki/Anti-pattern).

### `copyPipedHeaders`

**Type: `boolean`**\
**Default: `false`**

Automatically copy headers from piped streams.

When piping a request into a Got stream (e.g., `request.pipe(got.stream(url))`), this controls whether headers from the source stream are automatically merged into the Got request headers.

**Note:** Explicitly set headers take precedence over piped headers. Piped headers are only copied when a header is not already explicitly set.

Useful for proxy scenarios when explicitly enabled. Got automatically omits `host`, `authorization`, `cookie`, `cookie2`, `set-cookie`, `set-cookie2`, hop-by-hop headers, and headers nominated by `Connection`/`Proxy-Connection`. Got cannot know which app-specific headers are sensitive. Leave `copyPipedHeaders` disabled and copy only safe headers manually, or explicitly omit those headers before piping. If you trust the upstream and want to forward credentials, pass them explicitly in `headers`.

**Example: Opt in to automatic header copying for proxy scenarios**

```js
import got from 'got';
import {pipeline} from 'node:stream/promises';

server.get('/proxy', async (request, response) => {
	const gotStream = got.stream('https://example.com', {
		copyPipedHeaders: true,
		// Explicit headers win over piped headers.
		// Add credentials here only when the upstream is trusted.
		headers: {
			host: 'example.com',
		}
	});

	await pipeline(request, gotStream, response);
});
```

**Example: Keep it disabled and manually copy only safe headers**

```js
import got from 'got';
import {pipeline} from 'node:stream/promises';

server.get('/proxy', async (request, response) => {
	const gotStream = got.stream('https://example.com', {
		headers: {
			'user-agent': request.headers['user-agent'],
			'accept': request.headers['accept'],
			// Explicitly NOT copying host, connection, authorization, etc.
		}
	});

	await pipeline(request, gotStream, response);
});
```

### `timeout`

**Type: `object`**

See the [Timeout API](6-timeout.md).

#### **Merge behavior:**
> - Overrides existing properties.

### `retry`

**Type: `object`**

See the [Retry API](7-retry.md).

#### **Merge behavior:**
> - Overrides existing properties.

### `hooks`

**Type: `object`**

See the [Hooks API](9-hooks.md).

#### **Merge behavior:**
> - Merges arrays via `[...hooksArray, ...next]`

### `encoding`

**Type: `string`**\
**Default: `undefined`**, which decodes the response as UTF-8

[Encoding](https://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodings) to be used on [`setEncoding`](https://nodejs.org/api/stream.html#stream_readable_setencoding_encoding) of the response data.

To get a [`Uint8Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array), you need to set `responseType` to `'buffer'` instead. Don't set this option to `null`.

A leading byte order mark is removed when decoding UTF-8 and UTF-16LE text, for `responseType: 'text'`, `responseType: 'json'`, the `.text()` and `.json()` shortcuts, and the default pagination `transform`. `rawBody` always keeps the original bytes.

```js
import got from 'got';

const response = await got('https://httpbin.org/anything', {
	encoding: 'base64'
}).text();

console.log(response);
//=> base64 string
```

#### **Note:**
> - This option does not affect streams! Instead, do:

```js
import got from 'got';

const stream = got.stream('https://httpbin.org/anything');

stream.setEncoding('base64');
stream.on('data', console.log);
```

### `responseType`

**Type: `'text' | 'json' | 'buffer'`**\
**Default: `'text'`**

The parsing method.

The promise also has `.text()`, `.json()` and `.buffer()` methods which return another Got promise for the parsed body.\
It's like setting the options to `{responseType: 'json', resolveBodyOnly: true}` but without affecting the main Got promise.

```js
import got from 'got';

const responsePromise = got('https://httpbin.org/anything');
const bufferPromise = responsePromise.buffer();
const jsonPromise = responsePromise.json();

const [response, buffer, json] = await Promise.all([responsePromise, bufferPromise, jsonPromise]);
// `response` is an instance of Got Response
// `buffer` is an instance of Uint8Array
// `json` is an object
```

> [!NOTE]
> When using streams, this option is ignored.

> [!NOTE]
> `'buffer'` will return the raw body bytes as a `Uint8Array`. Any modifications will also alter the result of `.text()` and `.json()`. Before overwriting it, please copy it first via `new Uint8Array(buffer)`.\
>  See https://github.com/nodejs/node/issues/27080

### `resolveBodyOnly`

**Type: `boolean`**\
**Default: `false`**

If `true`, the promise will return the [Response body](3-streams.md#response-2) instead of the [Response object](3-streams.md#response-2).

```js
import got from 'got';

const url = 'https://httpbin.org/anything';

// This:
const body = await got(url).json();

// is semantically the same as this:
const body = await got(url, {responseType: 'json', resolveBodyOnly: true});
```

### `context`

**Type: `object<string, unknown>`**\
**Default: `{}`**

**Note:**
> - Non-enumerable properties inside are **not** merged.

Contains user data. It's very useful for storing auth tokens:

```js
import got from 'got';

const instance = got.extend({
	hooks: {
		beforeRequest: [
			options => {
				if (typeof options.context.token !== 'string') {
					throw new Error('Token required');
				}

				options.headers.token = options.context.token;
			}
		]
	}
});

const context = {
	token: 'secret'
};

const {headers} = await instance('https://httpbin.org/headers', {context}).json();

console.log(headers);
//=> {token: 'secret', …}
```

This option is enumerable. In order to define non-enumerable properties inside, do the following:

```js
import got from 'got';

const context = {};

Object.defineProperties(context, {
	token: {
		value: 'secret',
		enumerable: false,
		configurable: true,
		writable: true
	}
});

const instance = got.extend({context});

console.log(instance.defaults.options.context);
//=> {}
```

#### **Merge behavior:**
> - Overrides existing properties.

### `cookieJar`

**Type: <code>object | [tough.cookieJar](https://github.com/salesforce/tough-cookie#cookiejar)</code>**

#### **Note:**
> - Setting this option will result in the `cookie` header being overwritten.

Cookie support. Handles parsing and storing automatically.

```js
import got from 'got';
import {CookieJar} from 'tough-cookie';

const cookieJar = new CookieJar();

await cookieJar.setCookie('foo=bar', 'https://example.com');
await got('https://example.com', {cookieJar});
```

#### `cookieJar.setCookie`

**Type: `(rawCookie: string, url: string) => void | Promise<void>`**

See [ToughCookie API](https://github.com/salesforce/tough-cookie#setcookiecookieorstring-currenturl-options-cberrcookie) for more information.

#### `cookieJar.getCookieString`

**Type: `(currentUrl: string) => string | Promise<string>`**

See [ToughCookie API](https://github.com/salesforce/tough-cookie#getcookiestring) for more information.

### `ignoreInvalidCookies`

**Type: `boolean`**\
**Default: `false`**

Ignore invalid cookies instead of throwing an error.\
Only useful when the `cookieJar` option has been set.

#### **Note:**
> - This is not recommended! Use at your own risk.

### `followRedirect`

**Type: `boolean | (response: PlainResponse) => boolean`**\
**Default: `true`**

Whether redirect responses should be followed automatically.

Optionally, pass a function to dynamically decide based on the response object. The function is only called for 3xx responses, and an unchanged function is evaluated only once per response.

#### **Note:**
> - If a `303` is sent by the server in response to any request type (POST, DELETE, etc.), Got will request the resource pointed to in the location header via GET.\
>  This is in accordance with the [specification](https://tools.ietf.org/html/rfc7231#section-6.4.4). You can optionally turn on this behavior also for other redirect codes - see [`methodRewriting`](#methodrewriting).
> - On cross-origin redirects, Got strips `host`, `cookie`, `cookie2`, `authorization`, and `proxy-authorization`, as well as any credentials embedded in the URL.
> - `307` and `308` redirects preserve the request method and replayable body, even across origins, as required by the specification. `QUERY` requests also preserve replayable bodies on `301` and `302` redirects because `QUERY` is safe and idempotent. Same-origin `301` and `302` redirects that keep the request method also replay the body. Got fails instead of following when the body cannot be replayed.
> - Other cross-origin redirects can drop the request body and request body headers to avoid forwarding payloads to another origin.
> - When a redirect rewrites the request to `GET`, Got also strips request body headers.
> - Use [`hooks.beforeRedirect`](9-hooks.md#beforeredirect) for app-specific sensitive headers.

```js
import got from 'got';

const instance = got.extend({followRedirect: false});

const response = await instance('http://google.com');

console.log(response.headers.location);
//=> 'https://google.com'
```

### `maxRedirects`

**Type: `number`**\
**Default: `10`**

If exceeded, the request will be aborted and a [`MaxRedirectsError`](8-errors.md#maxredirectserror) will be thrown.

```js
import got from 'got';

const instance = got.extend({maxRedirects: 3});

try {
	await instance('https://nghttp2.org/httpbin/absolute-redirect/5');
} catch (error) {
	//=> 'Redirected 3 times. Aborting.'
	console.log(error.message);
}
```

### `decompress`

**Type: `boolean`**\
**Default: `true`**

Decompress the response automatically. This will set the `accept-encoding` header to `gzip, deflate, br` (and `zstd` on Node.js >= 22.15.0).

If disabled, a compressed response is returned as a `Uint8Array`. This may be useful if you want to handle decompression yourself.

> [!NOTE]
> Zstandard (`zstd`) compression support is available on Node.js >= 22.15.0 and will be automatically enabled when available.

```js
import got from 'got';

const response = await got('https://google.com');

console.log(response.headers['content-encoding']);
//=> 'gzip'
```

### `strictContentLength`

**Type: `boolean`**\
**Default: `true`**

Throw an error if the server response's `content-length` header value doesn't match the number of bytes received.

This is useful for detecting truncated responses and follows RFC 9112 requirements for message completeness.

> [!NOTE]
> - Responses without a `content-length` header are not validated.
> - When enabled and validation fails, a [`ReadError`](8-errors.md#readerror) with code `ERR_HTTP_CONTENT_LENGTH_MISMATCH` will be thrown.
> - Keep custom native responses in byte mode for exact validation. Calling `setEncoding()` on the native response can lose the original byte count when decoding malformed input.

### `dnsLookup`

**Type: `Function`**\
**Default: [`dns.lookup`](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback)**

Custom DNS resolution logic.

The function signature is the same as `dns.lookup`.

### `dnsCache`

**Type: <code>{lookup: Function; clear?: Function} | boolean</code>**

A DNS cache instance used for making DNS lookups.\
Useful when making lots of requests to different public hostnames.
Set to `true` to use Got's shared DNS cache.
When using `got.extend()`, set to `false` to opt out of a DNS cache configured by the parent instance.

**Note:**
> - This should stay disabled when making requests to internal hostnames such as localhost, database.local etc.
> - Got's built-in DNS cache uses `dns.resolve4(…)` and `dns.resolve6(…)` under the hood and falls back to `dns.lookup(…)` when no DNS records are found, which may lead to additional delay.
> - Because Got's built-in DNS cache resolves A and AAAA records separately, it cannot preserve OS-specific `verbatim` address ordering from `dns.lookup(…)`.
> - If present, `clear(hostname?)` can be called by user code to clear cached entries.

### `dnsLookupIpVersion`

**Type: `4 | 6`**\
**Default: `undefined`**

The IP version to use. Specifying `undefined` will use the default configuration.

### `request`

**Type: <code>Function<[ClientRequest](https://nodejs.org/api/http.html#http_class_http_clientrequest) | [IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage) | undefined> | AsyncFunction<[ClientRequest](https://nodejs.org/api/http.html#http_class_http_clientrequest) | [IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage) | undefined></code>**\
**Default: Got's built-in HTTP/1.1 or HTTP/2 request implementation**

Custom request function.

Returning `undefined` (or resolving to `undefined`) will fall back to Got's native request implementation.

### `cache`

**Type: `object | false`**\
**Default: `undefined`**

[Cache adapter instance](cache.md) for storing cached response data.

### `cacheOptions`

**Type: `object`**\
**Default: `{}`**

[Cache options](https://github.com/kornelski/http-cache-semantics#constructor-options) used for the specified request.

### `http2`

**Type: `boolean`**\
**Default: `false`**

If `true`, Got will use its built-in HTTP/2 client when ALPN selects HTTP/2.

**Note:**
> - ALPN negotiation will take place in order to determine if the server actually supports HTTP/2. If it doesn't, HTTP/1.1 will be used. When a custom `agent.https` instance is set, Got uses that native HTTPS agent directly and skips HTTP/2 negotiation.

**Note:**
> - If the `request` option returns a request or response, it controls the transport and Got's HTTP/2 client is bypassed. Return `undefined` to fall back to Got's built-in transport.

**Note:**
> - There is no direct [`h2c` Upgrade](https://datatracker.ietf.org/doc/html/rfc9113#section-11.2) support. However, you can provide a `h2session` option in a `beforeRequest` hook. See [an example](examples/h2c.js).

```js
import got from 'got';

const {statusCode} = await got(
	'https://httpbin.org/anything',
	{
		http2: true
	}
);

console.log(statusCode);
//=> 200
```

### `agent`

**Type: `object`**\
**Default: `{}`**

An object with `http`, `https` and `http2` properties.

Got will automatically resolve the protocol and use the corresponding agent. HTTP/2 uses Got's internal session pool by default. Set `agent.http2` to `false` to disable HTTP/2 session pooling for the request. `agent.http2` is only a pooling opt-out flag; custom HTTP/2 agents are not part of the public API. When `http2` is enabled, a custom `agent.https` instance makes Got use the native HTTP/1.1 request path because Got's built-in HTTP/2 session pool does not support custom HTTPS agents.

```js
{
	http: http.globalAgent,
	https: https.globalAgent
}
```

### `throwHttpErrors`

**Type: `boolean`**\
**Default: `true`**

If `true`, it will [throw](8-errors.md#httperror) when the status code is not `2xx` / `3xx`.

If this is disabled, requests that encounter an error status code will be resolved with the response instead of throwing. This may be useful if you are checking for resource availability and are expecting error responses.

### `username`

**Type: `string`**\
**Default: `''`**

The `username` used for [Basic authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).

### `password`

**Type: `string`**\
**Default: `''`**

The `password` used for [Basic authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).

### `localAddress`

**Type: `string | undefined`**\
**Default: `undefined`**

The local IP address used to make the request.

### `createConnection`

**Type: `Function | undefined`**\
**Default: `undefined`**

The function used to retrieve a `net.Socket` instance when the `agent` option is not used.

For HTTP/1, either return the socket synchronously or call `callback(null, socket)` asynchronously. Call `callback(error)` if socket creation fails.

When `http2` is enabled for HTTPS, this function must synchronously return a `tls.TLSSocket`. Callback-based creation is not supported by HTTP/2 negotiation.

### `https`

**Type: `object`**

See [Advanced HTTPS API](5-https.md).

### `pagination`

**Type: `object`**

See [Pagination API](4-pagination.md).

### `setHost`

**Type: `boolean`**\
**Default: `true`**

Specifies whether or not to automatically add the `Host` header.

### `maxHeaderSize`

**Type: `number | undefined`**\
**Default: `undefined`**

Optionally overrides the value of [`--max-http-header-size`](https://nodejs.org/api/cli.html#cli_max_http_header_size_size) (default 16KB: `16384`).

### `methodRewriting`

**Type: `boolean`**\
**Default: `false`**

Specifies if the HTTP request method should be [rewritten as `GET`](https://tools.ietf.org/html/rfc7231#section-6.4) on redirects.

As the [specification](https://tools.ietf.org/html/rfc7231#section-6.4) prefers to rewrite the HTTP method only on `303` responses, this is Got's default behavior. Cross-origin `301` and `302` redirects also rewrite `POST` requests to `GET` by default to avoid forwarding request bodies to another origin. Setting `methodRewriting` to `true` will also rewrite same-origin `301` and `302` responses, as allowed by the spec. This is the behavior followed by `curl` and browsers.

**Note:**
> - Got never performs method rewriting on `307` and `308` responses, as this is [explicitly prohibited by the specification](https://www.rfc-editor.org/rfc/rfc7231#section-6.4.7).

### `enableUnixSockets`

**Type: `boolean`**\
**Default: `false`**

When enabled, requests can also be sent via [UNIX Domain Sockets](https://serverfault.com/questions/124517/what-is-the-difference-between-unix-sockets-and-tcp-ip-sockets).

> **Warning**
> Make sure you do your own URL sanitizing if you accept untrusted user input for the URL.

Use the following URL scheme: `PROTOCOL://unix:SOCKET:PATH`

- `PROTOCOL` - `http` or `https`
- `SOCKET` - Absolute path to a UNIX domain socket, for example: `/var/run/docker.sock`
- `PATH` - Request path, for example: `/v2/keys`

```js
import got from 'got';

await got('http://unix:/var/run/docker.sock:/containers/json', {enableUnixSockets: true});

// Or without protocol (HTTP by default)
await got('unix:/var/run/docker.sock:/containers/json', {enableUnixSockets: true});

// Enable Unix sockets for the whole instance.
const gotWithUnixSockets = got.extend({enableUnixSockets: true});

await gotWithUnixSockets('http://unix:/var/run/docker.sock:/containers/json');
```

## Methods

### `options.merge(other: Options | OptionsInit)`

Merges `other` into the current instance.

If you look at the [source code](../source/core/options.ts), you will notice that options track internal merge state.\
Setters work a bit differently while merge is in progress.

### `options.toJSON()`

Returns a new plain object that can be stored as [JSON](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#tojson_behavior).

### `options.createNativeRequestOptions()`

Creates a new object for native Node.js HTTP request options.

In other words, this translates Got options into Node.js options.

**Note:**
> - Some other stuff, such as timeouts, is handled internally by Got.

### `options.getRequestFunction()`

Returns a [`http.request`-like](https://nodejs.org/api/http.html#http_http_request_url_options_callback) function used to make the request.

### `options.freeze()`

Makes the entire `Options` instance read-only.

---

### Documentation 3 Streams (`documentation/3-streams.md`)

[> Back to homepage](../readme.md#documentation)

## Stream API

Source code: [`source/core/index.ts`](../source/core/index.ts)

### `got.stream(url, options, defaults)`

The function above is exposed by the `got` main interface and returns a new instance of `Request`.

### `new Request(url, options, defaults)`

**Extends: [`Duplex` stream](https://nodejs.org/api/stream.html#stream_class_stream_duplex)**

This constructor takes the same arguments as the Got promise.

**Note:**
> When piping to [`ServerResponse`](https://nodejs.org/api/http.html#http_class_http_serverresponse), the headers will be automatically copied, except for hop-by-hop headers like `connection`, `keep-alive`, `transfer-encoding`, and any headers listed in `connection`.\
> When `decompress` is `true` (default) and the response is compressed, the `content-encoding` and `content-length` headers are not copied, as the response is decompressed.\
> To filter which headers are copied, listen to the `response` event and modify `response.headers` before piping to the destination.

**Note:**
> If the `body`, `json` or `form` option is used, this stream will be read-only. Check [`stream.isReadonly`](#streamisreadonly) to detect this condition.

**Note:**
> - While `got.post('https://example.com')` resolves, `got.stream.post('https://example.com')` will hang indefinitely until a body is provided.
> - If there's no body on purpose, remember to `stream.end()` or set the body option to an empty string.
> - `got.stream` does not auto-end for `POST`, `PUT`, `OPTIONS`, `PATCH`, `DELETE`, or `QUERY`. Call `stream.end()` when you are not piping a body.

```js
import stream from 'node:stream';
import {pipeline as streamPipeline} from 'node:stream/promises';
import fs from 'node:fs';
import got from 'got';

// This example streams the GET response of a URL to a file.
await streamPipeline(
	got.stream('https://sindresorhus.com'),
	fs.createWriteStream('index.html')
);

// For payload-capable shortcut methods such as POST, PUT, PATCH, DELETE, and QUERY, `got.stream` returns a `stream.Writable`.
// This example POSTs the contents of a file to a URL.
await streamPipeline(
	fs.createReadStream('index.html'),
	got.stream.post('https://sindresorhus.com'),
	new stream.PassThrough()
);

// In order to send one of these methods without a request body, explicitly specify an empty body:
await streamPipeline(
	got.stream.post('https://sindresorhus.com', { body: '' }),
	new stream.PassThrough()
)
```

Please note that `new stream.PassThrough()` is required in order to catch read errors.\
If it was missing then `pipeline` wouldn't catch any read errors because there would be no stream to pipe to.\
In other words, it would only check errors when writing.

**Tip:**
> - Avoid `from.pipe(to)` as it doesn't forward errors.

### `stream.options`

**Type: [`Options`](2-options.md)**

The options used to make the request.

### `stream.response`

**Type: [`IncomingMessage`](https://nodejs.org/api/http.html#http_class_http_incomingmessage)**

The underlying `IncomingMessage` instance.

### `stream.requestUrl`

**Type: [`URL`](https://nodejs.org/api/url.html#url_the_whatwg_url_api)**

The current `URL` object in this try.

### `stream.redirectUrls`

**Type: [`URL[]`](https://nodejs.org/api/url.html#url_the_whatwg_url_api)**

An array of URLs of consecutive requests.

### `stream.retryCount`

**Type: `number`**

The current retry count.

**Note:**
> - Must be overriden when retrying.

### `stream.ip`

**Type: `string | undefined`**

The destination IP address.

### `stream.isAborted`

**Type: `boolean`**

Whether the request has been aborted or not.

### `stream.socket`

**Type: `net.Socket | tls.Socket | undefined`**

The socket used for this particular request.

### `stream.downloadProgress`

**Type: [`Progress`](typescript.md#progress)**

An object representing how much data have been downloaded.

### `stream.uploadProgress`

**Type: [`Progress`](typescript.md#progress)**

An object representing how much data have been uploaded.

Granular upload progress is automatically supported for non-stream `body` (string/Uint8Array/TypedArray), `json`, and `form` options. For Node.js streams, you can use the [`chunk-data`](https://github.com/sindresorhus/chunk-data) package to get granular progress:

```js
import fs from 'node:fs';
import got from 'got';
import {chunkFromAsync} from 'chunk-data';

const filePath = 'large-file.bin';
const stream = fs.createReadStream(filePath);
const size = fs.statSync(filePath).size;

await got.post('https://httpbin.org/anything', {
	body: chunkFromAsync(stream, 65_536),
	headers: {
		'content-length': size
	}
})
.on('uploadProgress', progress => {
	console.log(progress);
});
```

### `stream.timings`

**Type: [`Timings`](typescript.md#timings)**

An object representing performance information.

Got computes the timings itself, with the same phases [`http-timer`](https://github.com/szmarczak/http-timer) reports.

### `stream.isFromCache`

**Type: `boolean | undefined`**

Whether the response has been fetched from cache.

### `stream.reusedSocket`

**Type: `boolean`**

Whether the socket was used for other previous requests.

### `stream.isReadonly`

**Type: `boolean`**

Whether the stream is read-only. Returns `true` when `body`, `json`, or `form` options are provided.

## Events

### `stream.on('response', …)`

#### `response`

**Type: [`PlainResponse`](typescript.md#plainresponse)**

This is emitted when a HTTP response is received.

```js
import {pipeline as streamPipeline} from 'node:stream/promises';
import {createWriteStream} from 'node:fs';
import got from 'got';

const readStream = got.stream('http://example.com/image.png', {throwHttpErrors: false});

const onError = error => {
	// Do something with it.
};

readStream.on('response', async response => {
	if (response.headers.age > 3600) {
		console.log('Failure - response too old');
		readStream.destroy(); // Destroy the stream to prevent hanging resources.
		return;
	}

	// Prevent `onError` being called twice.
	readStream.off('error', onError);

	try {
		await streamPipeline(
			readStream,
			createWriteStream('image.png')
		);

		console.log('Success');
	} catch (error) {
		onError(error);
	}
});

readStream.once('error', onError);
```

### Example: Reading HTTP error response bodies

By default, Got throws HTTP errors before the stream becomes readable. To read error response bodies:

```js
import {pipeline as streamPipeline} from 'node:stream/promises';
import got from 'got';

const stream = got.stream('https://httpbin.org/status/404', {
	throwHttpErrors: false
});

stream.on('response', response => {
	if (!response.ok) {
		console.log(`HTTP Error: ${response.statusCode}`);
		// Stream is readable, you can pipe or read the error body
	}
});

await streamPipeline(stream, process.stdout);
```

**Example: Filter headers when proxying to ServerResponse**

```js
import {pipeline as streamPipeline} from 'node:stream/promises';
import got from 'got';
import express from 'express';

const app = express();

// Allowlist specific headers when proxying
app.get('/proxy', async (request, response) => {
	await streamPipeline(
		got.stream(request.query.url).on('response', upstreamResponse => {
			// Only allow specific headers
			for (const header of Object.keys(upstreamResponse.headers)) {
				if (!['content-type', 'content-length'].includes(header.toLowerCase())) {
					delete upstreamResponse.headers[header];
				}
			}
		}),
		response
	);
});
```

### `stream.on('downloadProgress', …)`

#### `progress`

**Type: [`Progress`](typescript.md#progress)**

This is emitted on every time `stream.downloadProgress` is updated.

### `stream.on('uploadProgress', …)`

#### `progress`

**Type: [`Progress`](typescript.md#progress)**

This is emitted on every time `stream.uploadProgress` is updated.

<a name="retry"></a>
### `stream.on('retry', …)`

To enable retrying when using streams, a retry handler must be attached.

When this event is emitted, you should reset the stream you were writing to and prepare the body again.

**Note:**
> - [`HTTPError`s](./8-errors.md#httperror) cannot be retried if [`options.throwHttpErrors`](./2-options.md#throwhttperrors) is `false`.
>   This is because stream data is saved to `error.response.body` and streams can be read only once.
> - For the Promise API, there is no such limitation.
> - If you need to read HTTP error response bodies without retry, see [Reading HTTP error response bodies](#example-reading-http-error-response-bodies).

#### `retryCount`

**Type: `number`**

The current retry count.

#### `error`

**Type: [`RequestError`](8-errors.md#requesterror)**

The error that caused this retry.

#### `createRetryStream`

**Type: `(options?: OptionsInit) => Request`**

```js
import fs from 'node:fs';
import got from 'got';

let writeStream;

const fn = retryStream => {
	const options = {
		headers: {
			foo: 'bar'
		},
	};

	const stream = retryStream ?? got.stream('https://example.com', options);

	if (writeStream) {
		writeStream.destroy();
	}

	writeStream = fs.createWriteStream('example-com.html');

	stream.pipe(writeStream);

	// If you don't attach the listener, it will NOT make a retry.
	// It automatically checks the listener count so it knows whether to retry or not :)
	stream.once('retry', (retryCount, error, createRetryStream) => {
		fn(createRetryStream()); // or: fn(createRetryStream(optionsToMerge))
	});
};

fn();
```

### `stream.on('redirect', …)`

#### `updatedOptions`

**Type: [`Options`](2-options.md)**

The new options used to make the next request.

#### `response`

**Type: [`IncomingMessage`](https://nodejs.org/api/http.html#http_class_http_incomingmessage)**

The `IncomingMessage` instance the redirect came from.

## Internal usage

These are the functions used internally by Got.\
Other non-documented functions are private and should not be accessible.

### `stream.flush()`

This function is executed automatically by Got. It marks the current stream as ready. If an error occurs before `stream.flush()` is called, it's thrown immediately after `stream.flush()`.

### `stream._beforeError(error)`

This function is called instead of `stream.destroy(error)`, required in order to execute async logic, such as reading the response (e.g. when `ERR_NON_2XX_3XX_RESPONSE` occurs).

### `stream._noPipe`

**Type: `boolean`**

Whether piping is disabled or not. This property is used by the Promise API.

---

## `Response`

Source code: [`source/core/response.ts`](../source/core/response.ts)

**Extends: [`IncomingMessage`](https://nodejs.org/api/http.html#http_class_http_incomingmessage)**

### `requestUrl`

**Type: `URL`**

The original request URL. It is the first argument when calling `got(…)`.

### `redirectUrls`

**Type: `URL[]`**

The redirect URLs.

### `request`

**Type: `Request`**

The underlying Got stream.

### `ip`

**Type: `string`**

The server's IP address.

**Note:**
> - Not available when the response is cached.

### `isFromCache`

**Type: `boolean`**

Whether the response comes from cache or not.

### `ok`

**Type: `boolean`**

Whether the response was successful

**Note:**
> - A request is successful when the status code of the final request is `2xx` or `3xx`.
> - When [following redirects](2-options.md#followredirect), a request is successful **only** when the status code of the final request is `2xx`.
> - `304` responses are always considered successful.
> - Got throws automatically when `response.ok` is `false` and `throwHttpErrors` is `true`.
> - **To read HTTP error response bodies with streams**, set `throwHttpErrors: false` and check `response.ok` in the `response` event handler. [See example above](#example-reading-http-error-response-bodies).

### `statusCode`

**Type: `number`**

The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status).

### `url`

**Type: `string`**

The final URL after all redirects.

### `timings`

**Type: [`Timings`](typescript.md#timings) `| undefined`**

The object contains the following properties:

- `start` - Time when the request started.
- `socket` - Time when a socket was assigned to the request.
- `lookup` - Time when the DNS lookup finished.
- `connect` - Time when the socket successfully connected.
- `secureConnect` - Time when the socket securely connected.
- `upload` - Time when the request finished uploading.
- `response` - Time when the request fired `response` event.
- `end` - Time when the response fired `end` event.
- `error` - Time when the request fired `error` event.
- `abort` - Time when the request fired `abort` event.
- `phases`
	- `wait` - `timings.socket - timings.start`
	- `dns` - `timings.lookup - timings.socket`
	- `tcp` - `timings.connect - timings.lookup`
	- `tls` - `timings.secureConnect - timings.connect`
	- `request` - `timings.upload - (timings.secureConnect || timings.connect)`
	- `firstByte` - `timings.response - timings.upload`
	- `download` - `timings.end - timings.response`
	- `total` - `(timings.end || timings.error || timings.abort) - timings.start`

If something has not been measured yet, it will be `undefined`.

The entire property is `undefined` for cached responses and responses returned directly by hooks or custom request functions without timing information.

__Note__: The time is a `number` representing the milliseconds elapsed since the UNIX epoch.

### `retryCount`

**Type: `number`**

The same as `request.retryCount`.

### `rawBody`

**Type: `Uint8Array`**

**Note:**
> - This property is only accessible when using Promise API.

The raw response body bytes.

### `body`

**Type: `unknown`**

**Note:**
> - This property is only accessible when using Promise API.

The parsed response body.

### `aborted`

**Type: `boolean`**

Node's `IncomingMessage.aborted`: whether the response was aborted before it completed.

> [!NOTE]
> Over HTTP/2, a stream reset with an error code before the response ended fails the request, either as an aborted response or as a stream error. A reset with `NO_ERROR` is harder: it is how a server stops an upload it does not need, and it can come with or without the end of the response. Got accepts such a response when it saw the end of the response first, or when the `content-length` header proves it complete. While the client is still uploading, it reports any other response as aborted. Detecting this is best effort for a response without a `content-length` header: when the end of the response and the reset arrive together, a complete response can be reported as aborted. A `NO_ERROR` reset after the upload has finished, such as for a `GET`, cannot be told apart from the end of the response, so a body without a `content-length` header cut off that way is delivered as complete.

### `complete`

**Type: `boolean`**

If `true`, the response has been fully parsed.

### `socket`

**Type: `net.Socket | tls.TLSSocket`**

The same as `request.socket`.

### `headers`

**Type: `object<string, string>`**

The [response headers](https://nodejs.org/api/http.html#http_message_headers).

### `statusMessage`

**Type: `string`**

The status message corresponding to the status code.

---

### Documentation 4 Pagination (`documentation/4-pagination.md`)

[> Back to homepage](../readme.md#documentation)

## Pagination API

Source code: [`source/create.ts`](../source/create.ts)

### How does the `Link` header work?

The [RFC5988](https://datatracker.ietf.org/doc/html/rfc5988#section-5) defines how the `Link` header looks like.

When the response has been processed, Got looks for [the reference of the `next` relation](https://datatracker.ietf.org/doc/html/rfc5988#section-6.2.2).\
This way Got knows the URL it should visit afterwards. The header can look like this:

```text
Link: <https://api.github.com/repositories/18193978/commits?page=2>; rel="next", <https://api.github.com/repositories/18193978/commits?page=44>; rel="last"
```

By default, Got looks only at the `next` relation. To use [other relations](https://datatracker.ietf.org/doc/html/rfc5988#section-6.2.2), you need to customize the `paginate` function below.

### `got.paginate(url, options?)`
### `got.paginate.each(url, options?)`

Returns an [async iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of).

This is memory efficient, as the logic is executed immediately when new data comes in.

```js
import got from 'got';

const countLimit = 10;

const pagination = got.paginate(
	'https://api.github.com/repos/sindresorhus/got/commits',
	{
		pagination: {countLimit}
	}
);

console.log(`Printing latest ${countLimit} Got commits (newest to oldest):`);

for await (const commitData of pagination) {
	console.log(commitData.commit.message);
}
```

### `got.paginate.all(url, options?)`

**Note:**
> - Querying a large dataset significantly increases memory usage.

Returns a Promise for an array of all results.

```js
import got from 'got';

const countLimit = 10;

const results = await got.paginate.all('https://api.github.com/repos/sindresorhus/got/commits', {
	pagination: {countLimit}
});

console.log(`Printing latest ${countLimit} Got commits (newest to oldest):`);
console.log(results);
```

### `parseLinkHeader(link)`

**Type: `(link: string) => Array<{reference: string; parameters: Record<string, string>}>`**

Parses a [`Link`](https://www.rfc-editor.org/rfc/rfc8288) header into one entry per link. It is exported from Got so the default `pagination.paginate` can be copied or replaced.

> [!NOTE]
> - Link targets keep their original case, but parameter names are lowercased, so read `parameters.rel` rather than `parameters.REL`.
> - The first occurrence of a repeated parameter wins, and a parameter may be present without a value, in which case its value is an empty string.
> - A link without any parameters parses to an empty `parameters` object instead of throwing.
> - Values keep their surrounding quotes, so `rel="next"` yields the value `"next"` including the quotes.
> - `parameters` has no prototype, so `parameters.toString` is `undefined` and inherited names such as `constructor` are only present when the header sets them.

```js
import {parseLinkHeader} from 'got';

console.log(parseLinkHeader('<https://example.com/2>; rel="next"'));
//=> [{reference: 'https://example.com/2', parameters: {rel: '"next"'}}]
```

### `pagination`

**Type: `object`**\
**Default:**

```js
{
	transform: (response: Response) => {
		if (response.request.options.responseType === 'json') {
			return response.body;
		}

		const body = response.request.options.responseType === 'buffer'
			? decodeUint8Array(response.body as Uint8Array, response.request.options.encoding)
			: response.body as string;

		return response.request.options.parseJson(body);
	},
	paginate: ({response}) => {
		const rawLinkHeader = response.headers.link;
		if (typeof rawLinkHeader !== 'string' || rawLinkHeader.trim() === '') {
			return false;
		}

		const parsed = parseLinkHeader(rawLinkHeader);
		const next = parsed.find(entry => !Object.hasOwn(entry.parameters, 'anchor') && entry.parameters.rel?.replaceAll('"', '').toLowerCase().split(/\s+/v).includes('next'));

		if (next) {
			return {
				url: next.reference
			};
		}

		return false;
	},
	filter: () => true,
	shouldContinue: () => true,
	countLimit: Number.POSITIVE_INFINITY,
	backoff: 0,
	requestLimit: 10_000,
	stackAllItems: false
}
```

This option represents the `pagination` object.

The default `paginate` uses the exported [`parseLinkHeader(…)`](#parselinkheaderlink) helper.

#### `transform`

**Type: `Function`**\
**Default:** Parses text and buffer responses as JSON using the configured encoding, and returns JSON responses unchanged.

A function that transforms [`Response`](3-streams.md#response-2) into an array of items.\
This is where you should do the parsing.

#### `paginate`

**Type: `Function`**\
**Default: `Link` header logic**

By default, links with an `anchor` parameter are ignored, as permitted by [RFC 8288, section 3.2](https://www.rfc-editor.org/rfc/rfc8288#section-3.2).

The function takes an object with the following properties:

- `response` - The current response object,
- `currentItems` - Items from the current response,
- `allItems` - An empty array, unless `stackAllItems` is `true`, otherwise it contains all emitted items.

It should return an object representing Got options pointing to the next page. If there is no next page, `false` should be returned instead. The function may also be async and return a promise for either value.

The options are merged automatically with the previous request.\
Therefore the options returned by `pagination.paginate(…)` must reflect changes only.

**Note:**
> - The `url` option (if set) accepts a string or [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) instance. Relative strings resolve against the current request URL. After a cross-origin redirect, that means the final redirected URL, not the original `prefixUrl`.
> - When pagination navigates to a different origin, Got strips inherited sensitive headers such as `authorization`, `cookie`, and `proxy-authorization`. If you trust the next-page URL and want to forward a sensitive header, return it explicitly from `pagination.paginate(…)`.

#### `filter`

**Type: `Function`**\
**Default: `({item, currentItems, allItems}) => true`**

Whether the item should be emitted or not.

#### `shouldContinue`

**Type: `Function`**\
**Default: `({item, currentItems, allItems}) => true`**

**Note:**
> - This function executes only when `filter` returns `true`.

For example, if you need to stop before emitting an entry with some flag, you should use `({item}) => !item.flag`.

If you want to stop after emitting the entry, you should use `({item, allItems}) => allItems.some(item => item.flag)` instead.

#### `countLimit`

**Type: `number`**\
**Default: `Number.POSITIVE_INFINITY`**

The maximum amount of items that should be emitted.

#### `backoff`

**Type: `number`**\
**Default: `0`**

Milliseconds to wait before the next request is triggered.

#### `requestLimit`

**Type: `number`**\
**Default: `10000`**

The maximum amount of request that should be triggered.

**Note:**
> - [Retries on failure](7-retry.md) are not counted towards this limit.

#### `stackAllItems`

**Type: `boolean`**\
**Default: `false`**

Defines how `allItems` is managed in `pagination.paginate`, `pagination.filter` and `pagination.shouldContinue`.

By default, `allItems` is always an empty array. Setting this to `true` will significantly increase memory usage when working with a large dataset.

### Example

In this example we will use `searchParams` instead of `Link` header.\
Just to show how you can customize the `paginate` function.

The reason `filter` looks exactly the same like `shouldContinue` is that the latter will tell Got to stop once we reach our timestamp.
The `filter` function is needed as well, because in the same response we can get results with different timestamps.

```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### Documentation 5 Https (`documentation/5-https.md`)

[> Back to homepage](../readme.md#documentation)

## Advanced HTTPS API

### `https`

**Type: `object`**

This option represents the options used to make HTTPS requests.

#### `alpnProtocols`

**Type: `string[]`**\
**Default: `undefined`**

Acceptable [ALPN](https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation) protocols.

By default no ALPN protocols are offered. When the `http2` option is `true`, the HTTP/2 negotiation path offers `['h2', 'http/1.1']` unless this option is set.

#### `rejectUnauthorized`

**Type: `boolean`**\
**Default: `true`**

If `true`, it will throw on invalid certificates, such as expired or self-signed ones.

#### `checkServerIdentity`

**Type: `(hostname: string, certificate: DetailedPeerCertificate) => Error | undefined`**\
**Default: `tls.checkServerIdentity`**

Custom check of the certificate. Useful for pinning certificates.

The function must return `undefined` if the check succeeded.\
If it failed, an `Error` should be returned.

**Note:**
> - In order to have the function called, the certificate must not be expired, self-signed nor with an untrusted-root.

Check [Node.js docs](https://nodejs.org/api/https.html#https_https_request_url_options_callback) for an example.

#### `serverName`

**Type: `string`**\
**Default: `undefined`**

Server name for the [Server Name Indication (SNI)](https://en.wikipedia.org/wiki/Server_Name_Indication) TLS extension.

This is useful when requesting to servers that don't have a proper domain name but use a certificate with a known CN/SAN.

```js
import got from 'got';

// Request to IP address with specific servername for TLS
await got('https://192.168.1.100', {
	https: {
		serverName: 'example.com'
	}
});
```

#### `certificateAuthority`

**Type: `string | Uint8Array | string[] | Uint8Array[]`**

**Note:**
> - The option has been renamed from the [`ca` TLS option](https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options) for better readability.

Overrides trusted [CA](https://en.wikipedia.org/wiki/Certificate_authority) certificates.

Defaults to CAs provided by [Mozilla](https://ccadb-public.secure.force.com/mozilla/IncludedCACertificateReport).

```js
import got from 'got';

// Single Certificate Authority
await got('https://example.com', {
	https: {
		certificateAuthority: fs.readFileSync('./my_ca.pem')
	}
});
```

#### `key`

**Type: `string | Uint8Array | string[] | Uint8Array[] | object[]`**

Private keys in [PEM format](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail).

Multiple keys with different passphrases can be provided as an array of `{pem: <string | Uint8Array>, passphrase: <string>}`.

**Note:**
> - Encrypted keys will be decrypted with `https.passphrase`.

#### `passphrase`

**Type: `string`**

Shared passphrase used for a single private key and/or a PFX.

#### `certificate`

**Type: `string | Uint8Array | string[] | Uint8Array[]`**

**Note:**
> - The option has been renamed from the [`cert` TLS option](https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options) for better readability.

[Certificate chains](https://en.wikipedia.org/wiki/X.509#Certificate_chains_and_cross-certification) in [PEM format](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail).

One certificate chain should be provided per private key.

When providing multiple certificate chains, they do not have to be in the same order as their private keys in `https.key`.

#### `pfx`

**Type: `string | Uint8Array | Array<string | Uint8Array | object>`**

[PFX or PKCS12](https://en.wikipedia.org/wiki/PKCS_12) encoded private key and certificate chain. Using `https.pfx` is an alternative to providing `https.key` and `https.certificate` individually. A PFX is usually encrypted, then `https.passphrase` will be used to decrypt it.

Multiple PFX can be provided as an array containing strings, byte arrays, and objects like:

```ts
{
	buffer: string | Uint8Array,
	passphrase?: string
}
```

#### `certificateRevocationLists`

**Type: `string | Uint8Array | string[] | Uint8Array[]`**

**Note:**
> - The option has been renamed from the [`crl` TLS option](https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options) for better readability.

#### `secureOptions`

**Type: `number`**

Optionally affect the OpenSSL protocol behavior, which is not usually necessary. This should be used carefully if at all!

The value is a numeric bitmask of the `SSL_OP_*` options from OpenSSL.

For example, to allow connections to legacy servers that do not support secure renegotiation, you can use `crypto.constants.SSL_OP_LEGACY_SERVER_CONNECT`.

```js
import crypto from 'node:crypto';
import got from 'got';

// Allow connections to servers with legacy renegotiation
await got('https://legacy-server.com', {
	https: {
		secureOptions: crypto.constants.SSL_OP_LEGACY_SERVER_CONNECT
	}
});
```

### Other HTTPS options

[Documentation for the below options.](https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options)

- `ciphers`
- `dhparam`
- `signatureAlgorithms` (renamed from `sigalgs`)
- `minVersion`
- `maxVersion`
- `honorCipherOrder`
- `tlsSessionLifetime` (renamed from `sessionTimeout`)
- `ecdhCurve`

### Examples

```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### Documentation 6 Timeout (`documentation/6-timeout.md`)

[> Back to homepage](../readme.md#documentation)

## Timeout options

Source code: [`source/core/timed-out.ts`](../source/core/timed-out.ts)

It is a good practice to set a timeout to prevent hanging requests.\
By default, there is no timeout set.

Note: The [`retry` configuration](7-retry.md) also applies to timeouts.

**All numbers refer to milliseconds.**

```js
import got from 'got';

const {timings} = await got('https://example.com', {
	timeout: {
		lookup: 100,
		connect: 50,
		secureConnect: 50,
		socket: 1000,
		send: 10000,
		response: 1000
	}
});

// Alternatively:
const {timings} = await got('https://example.com', {
	timeout: {
		request: 10000
	}
});

console.log(timings);
// {
// 	start: 1625474926602,
// 	socket: 1625474926605,
// 	lookup: 1625474926610,
// 	connect: 1625474926617,
// 	secureConnect: 1625474926631,
// 	upload: 1625474926631,
// 	response: 1625474926638,
// 	end: 1625474926642,
// 	error: undefined,
// 	abort: undefined,
// 	phases: {
// 		wait: 3,
// 		dns: 5,
// 		tcp: 7,
// 		tls: 14,
// 		request: 0,
// 		firstByte: 7,
// 		download: 4,
// 		total: 40
// 	}
// }
```

### `timeout`

**Type: `object`**

This object describes the maximum allowed time for particular events.

#### `lookup`

**Type: `number`**

Starts when a socket is assigned.\
Ends when the hostname has been resolved.

Does not apply when using a Unix domain socket.\
Does not apply when passing an IP address.

It is preferred to not use any greater value than `100`.

#### `connect`

**Type: `number`**

Starts when lookup completes.\
Ends when the socket is fully connected.

If `lookup` does not apply to the request, this event starts when the socket is assigned and ends when the socket is connected.

#### `secureConnect`

**Type: `number`**

Starts when `connect` completes.\
Ends when the handshake process completes.

This timeout applies only to HTTPS requests.

#### `socket`

**Type: `number`**

Starts when the socket is connected.\
Resets when new data is transferred.

It is the same as [`request.setTimeout(timeout)`](https://nodejs.org/api/http.html#http_request_settimeout_timeout_callback) which calls [`socket.setTimeout(timeout)`](https://nodejs.org/api/net.html#socketsettimeouttimeout-callback) after a socket is assigned to this request and is connected.

#### `send`

**Type: `number`**

Starts when the socket is connected.\
Ends when all data have been written to the socket.

**Note:**
> - This does not assure the data have been received by the other end!
> - It only assures that the data have been passed to the underlying OS.

#### `response`

**Type: `number`**

Starts when request has been flushed.\
Ends when the headers are received.

#### `read`

**Type: `number`**

Starts when the headers are received.\
Ends when the response's `end` event fires.

#### `request`

**Type: `number`**

Starts when the request is initiated, after the `beforeRequest` hooks of the first request have run.\
Ends when the final response's `end` event fires.

In other words, this is the global timeout. Redirects share the same budget, including the time spent in `beforeRedirect` hooks, cookie lookups, and custom request functions between them.

Hooks may change `timeout.request` while the request is in flight. A value set in a `beforeRedirect` hook applies once that hook has finished, and a redirected `beforeRequest` hook can raise or disable it immediately.

---

### Documentation 7 Retry (`documentation/7-retry.md`)

[> Back to homepage](../readme.md#documentation)

## Retry API

**Note:**
> If you're looking for retry implementation using streams, check out the [Retry Stream API](3-streams.md#retry).

**Tip:**
> You can trigger a retry by throwing the [`RetryError`](8-errors.md#retryerror) in any hook.

**Tip:**
> The `afterResponse` hook exposes a dedicated function to retry with merged options. [Read more](9-hooks.md#afterresponse).

### `retry`

**Type: `object`**\
**Default:**

```js
{
	limit: 2,
	methods: [
		'GET',
		'PUT',
		'HEAD',
		'DELETE',
		'OPTIONS',
		'TRACE',
		'QUERY'
	],
	statusCodes: [
		408,
		413,
		429,
		500,
		502,
		503,
		504,
		521,
		522,
		524
	],
	errorCodes: [
		'ETIMEDOUT',
		'ECONNRESET',
		'EADDRINUSE',
		'ECONNREFUSED',
		'EPIPE',
		'ENOTFOUND',
		'ENETUNREACH',
		'EAI_AGAIN'
	],
	maxRetryAfter: undefined,
	calculateDelay: ({computedValue}) => computedValue,
	backoffLimit: Number.POSITIVE_INFINITY,
	noise: 100,
	enforceRetryRules: true
}
```

This option represents the `retry` object.

`enforceRetryRules` decides whether the limits above are applied before `calculateDelay` runs. See [`calculateDelay`](#calculatedelay).

Undefined settings preserve inherited values, except `maxRetryAfter`, where `undefined` restores the request timeout fallback.

#### `limit`

**Type: `number`**

The maximum retry count.

#### `methods`

**Type: `string[]`**

The allowed methods to retry on.

**Note:**
> - By default, Got does not retry on `POST`.

#### `statusCodes`

**Type: `number[]`**

**Note:**
> - Only [**unsuccessful**](8-errors.md#) requests are retried. In order to retry successful requests, use an [`afterResponse`](9-hooks.md#afterresponse) hook.

The allowed [HTTP status codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) to retry on.

#### `errorCodes`

**Type: `string[]`**

The allowed error codes to retry on.

- `ETIMEDOUT` - One of the [timeout limits](6-timeout.md) was reached.
- `ECONNRESET`- The connection was forcibly closed.
- `EADDRINUSE`- Could not bind to any free port.
- `ECONNREFUSED`- The connection was refused by the server.
- `EPIPE` - The remote side of the stream being written has been closed.
- `ENOTFOUND` - Could not resolve the hostname to an IP address.
- `ENETUNREACH` - No internet connection.
- `EAI_AGAIN` - DNS lookup timed out.

#### `maxRetryAfter`

**Type: `number | undefined`**\
**Default: `options.timeout.request`**

The upper limit of [`retry-after` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After). If `undefined`, it will use `options.timeout` as the value.

If the limit is exceeded, the request is aborted.

#### `calculateDelay`

**Type: `Function`**

```ts
(retryObject: RetryObject) => Promisable<number>
```

```ts
interface RetryObject {
	attemptCount: number;
	retryOptions: RetryOptions;
	error: RequestError;
	computedValue: number;
	retryAfter?: number;
}
```

The function used to calculate the delay before the next request is made. Returning `0` aborts the retry.

Delays above `2_147_483_647` milliseconds (Node.js's timer limit), including `Infinity`, abort the retry instead of triggering an immediate retry. This also applies to delays computed from `Retry-After`.

**Note:**
> - By default, retry rules are enforced before `calculateDelay` runs (`enforceRetryRules: true`), so this function is only called when a retry is allowed.
> - If you set `enforceRetryRules: false`, `calculateDelay` takes full control of retry behavior. In that mode, check `computedValue` and return `0` when it is `0` to preserve default retry safeguards.

**Tip:**
> - This is especially useful when you want to scale down the computed value.

```js
import got from 'got';

await got('https://httpbin.org/anything', {
	retry: {
		limit: 3,
		calculateDelay: ({computedValue}) => {
			// When computedValue is 0, the default logic says don't retry
			// (limit exceeded, non-retryable error, etc.)
			if (computedValue === 0) {
				return 0;
			}

			// Scale down the delay
			return computedValue / 10;
		}
	}
});
```

#### `backoffLimit`

**Type: `number`**

The upper limit of the exponential backoff. The `noise` is added after this limit is applied.

By default, the `computedValue` is calculated in the following way:

```ts
Math.max(1, Math.min((2 ** (attemptCount - 1)) * 1000, backoffLimit) + noise)
```

Computed delays are at least 1 millisecond. Set both `backoffLimit` and `noise` to `0` for immediate retries. Returning `0` from `calculateDelay` still aborts the retry.

The delay increases exponentially.\
In order to prevent this, you can set this value to a fixed value, such as `1000`.

#### `noise`

**Type: `number`**

The maximum acceptable retry noise in the range of `-100` to `+100`.

---

### Documentation 8 Errors (`documentation/8-errors.md`)

[> Back to homepage](../readme.md#documentation)

## Errors

Source code:
- [`source/core/errors.ts`](../source/core/errors.ts)
- [`source/as-promise/types.ts`](../source/as-promise/types.ts)
- [`source/core/response.ts`](../source/core/response.ts)

All Got errors contain various metadata, such as:

- `code` - A string like `ERR_NON_2XX_3XX_RESPONSE`,
- `options` - An instance of [`Options`](2-options.md),
- `request` - An instance of Got Stream,
- `response` (optional) - An instance of Got Response,
- `timings` (optional) - Points to `response.timings`.

#### Capturing async stack traces

Read the article [here](async-stack-traces.md).

> [!NOTE]
> - The error codes may differ when the root error has a `code` property set.
> - The root error will be propagated as is via the [`cause`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause) property.

### `RequestError`

**Code: `ERR_GOT_REQUEST_ERROR`**

When a request fails. Contains a `code` property with error class code, like `ECONNREFUSED`. All the errors below inherit this one.

### `CacheError`

**Code: `ERR_CACHE_ACCESS`**

When a cache method fails, for example, if the database goes down or there's a filesystem error.

### `ReadError`

**Code: `ERR_READING_RESPONSE_STREAM` or `ERR_HTTP_CONTENT_LENGTH_MISMATCH`**

When reading from response stream fails.

The error code will be `ERR_HTTP_CONTENT_LENGTH_MISMATCH` when the `strictContentLength` option is enabled and the server specifies a `content-length` header but the actual number of bytes received doesn't match.

### `ParseError`

**Code: `ERR_BODY_PARSE_FAILURE`**

When server response code is 2xx, and parsing body fails. Includes a `response` property.

### `UploadError`

**Code: `ERR_UPLOAD`**

When the request body is a stream and an error occurs while reading from that stream.

### `HTTPError`

**Code: `ERR_NON_2XX_3XX_RESPONSE`**

When the request is unsuccessful.

A request is successful when the status code of the final request is `2xx` or `3xx`.

When [following redirects](2-options.md#followredirect), a request is successful **only** when the status code of the final request is `2xx`.

> [!NOTE]
> `304` responses are always considered successful.

### `MaxRedirectsError`

**Code: `ERR_TOO_MANY_REDIRECTS`**

When the server redirects you more than ten times. Includes a `response` property.

### `UnsupportedProtocolError`

> [!NOTE]
> This error is not public.

**Code: `ERR_UNSUPPORTED_PROTOCOL`**

When given an unsupported protocol.

### `TimeoutError`

**Code: `ETIMEDOUT`**

When the request is aborted due to a [timeout](6-timeout.md). Includes an `event` (a string) property along with `timings`.

### `RetryError`

**Code: `ERR_RETRYING`**

Always triggers a new retry when thrown.

### `AbortError`

**Code: `ERR_ABORTED`**

When the request is aborted with [AbortController.abort()](https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort).

---

### Documentation 9 Hooks (`documentation/9-hooks.md`)

[> Back to homepage](../readme.md#documentation)

## Hooks API

### `hooks`

**Type: `object<string, Function[]>`**

This option represents the hooks to run. Thrown errors will be automatically converted to [`RequestError`](8-errors.md#requesterror).

#### `init`

**Type: `InitHook[]`**\
**Default: `[]`**

```ts
(plainRequestOptions: OptionsInit, options: Options) => void
```

Called with the plain request options, right before their normalization.\
The second argument represents the current [`Options`](2-options.md) instance.

**Note:**
> - This hook must be synchronous.

**Note:**
> - This is called every time options are merged.

**Note:**
> - The `options` object may not have the `url` property. To modify it, use a `beforeRequest` hook instead.

**Note:**
> - This hook is called when a new instance of `Options` is created.
> - Do not confuse this with the creation of `Request` or `got(…)`.

**Note:**
> - When using `got(url)` or `got(url, undefined, defaults)` this hook will **not** be called.

This is especially useful in conjunction with `got.extend()` when the input needs custom handling.

For example, this can be used to fix typos to migrate from older versions faster.

```js
import got from 'got';

const instance = got.extend({
	hooks: {
		init: [
			plain => {
				if ('followRedirects' in plain) {
					plain.followRedirect = plain.followRedirects;
					delete plain.followRedirects;
				}
			}
		]
	}
});

// Normally, the following would throw:
const response = await instance(
	'https://example.com',
	{
		followRedirects: true
	}
);

// There is no option named `followRedirects`, but we correct it in an `init` hook.
```

Or you can create your own option and store it in a context:

```js
import got from 'got';

const instance = got.extend({
	hooks: {
		init: [
			(plain, options) => {
				if ('secret' in plain) {
					options.context.secret = plain.secret;
					delete plain.secret;
				}
			}
		],
		beforeRequest: [
			options => {
				options.headers.secret = options.context.secret;
			}
		]
	}
});

const {headers} = await instance(
	'https://httpbin.org/anything',
	{
		secret: 'passphrase'
	}
).json();

console.log(headers.Secret);
//=> 'passphrase'
```

#### `beforeRequest`

**Type: `BeforeRequestHook[]`**\
**Default: `[]`**

```ts
(options: NormalizedOptions, context: BeforeRequestHookContext) => Promisable<void | ClientRequest | IncomingMessage | ResponseLike>
```

Called right before making the request with `options.createNativeRequestOptions()`.\
This hook is especially useful in conjunction with `got.extend()` when you want to sign your request.

The second parameter is a context object with the following properties:
- `retryCount` - The current retry count (0 for the initial request, 1+ for retries).

**Note:**
> - Got will make no further changes to the request before it is sent.

**Note:**
> - Changing `options.json` or `options.form` has no effect on the request. You should change `options.body` instead. If needed, update the `options.headers` accordingly.

```js
import got from 'got';

const response = await got.post(
	'https://httpbin.org/anything',
	{
		json: {payload: 'old'},
		hooks: {
			beforeRequest: [
				(options, context) => {
					options.body = JSON.stringify({payload: 'new'});
					options.headers['content-length'] = new TextEncoder().encode(options.body).byteLength.toString();
				}
			]
		}
	}
);
```

You can use `context.retryCount` to conditionally modify behavior based on whether it's the initial request or a retry:

```js
import got from 'got';

const response = await got('https://httpbin.org/status/500', {
	retry: {
		limit: 2
	},
	hooks: {
		beforeRequest: [
			(options, context) => {
				// Only log on initial request, not on retries
				if (context.retryCount === 0) {
					console.log('Making initial request');
				}
			}
		]
	}
});
```

**Tip:**
> - You can indirectly override the `request` function by early returning a [`ClientRequest`-like](https://nodejs.org/api/http.html#http_class_http_clientrequest) instance or a [`IncomingMessage`-like](https://nodejs.org/api/http.html#http_class_http_incomingmessage) instance. This is very useful when creating a custom cache mechanism.
> - [Read more about this tip](cache.md#advanced-caching-mechanisms).

#### `beforeRedirect`

**Type: `BeforeRedirectHook[]`**\
**Default: `[]`**

```ts
(updatedOptions: Options, plainResponse: PlainResponse) => Promisable<void>
```

The equivalent of `beforeRequest` but when redirecting.

**Tip:**
> - This is especially useful when you want to avoid dead sites.
> - Use this to remove app-specific sensitive headers on redirect. Got already strips `host`, `cookie`, `cookie2`, `authorization`, and `proxy-authorization` on cross-origin redirects, and strips request body headers when a redirect rewrites the request to `GET`.

```js
import got from 'got';

const response = await got('https://example.com', {
	hooks: {
		beforeRedirect: [
			(options, response) => {
				if (options.url.hostname === 'dead-site.example') {
					options.url = 'https://fallback-site.example';
				}
			}
		]
	}
});
```

#### `beforeRetry`

**Type: `BeforeRetryHook[]`**\
**Default: `[]`**

```ts
(error: RequestError, retryCount: number) => Promisable<void>
```

The equivalent of `beforeError` but when retrying. Additionally, there is a second argument `retryCount`, the current retry number.

**Note:**
> - When using the Stream API, this hook is ignored.

**Note:**
> - When retrying, the `beforeRequest` hook is called afterwards.

**Note:**
> - If no retry occurs, the `beforeError` hook is called instead.

This hook is especially useful when you want to retrieve the cause of a retry.

```js
import got from 'got';

await got('https://httpbin.org/status/500', {
	hooks: {
		beforeRetry: [
			(error, retryCount) => {
				console.log(`Retrying [${retryCount}]: ${error.code}`);
				// Retrying [1]: ERR_NON_2XX_3XX_RESPONSE
			}
		]
	}
});
```

#### `beforeCache`

**Type: `BeforeCacheHook[]`**\
**Default: `[]`**

```ts
(response: Pick<PlainResponse, 'headers' | 'statusCode' | 'statusMessage'>) => false | void
```

Called right before the response is cached. Allows you to control caching behavior by modifying response properties or preventing caching entirely.

This is especially useful when you want to prevent caching of specific responses or modify cache headers.

**Return value:**
> - `false` - Prevent caching (remaining hooks are skipped)
> - `void`/`undefined` - Use default caching behavior (mutations take effect)

**Modifying the response:**
> - Hooks can directly mutate response properties like `headers`, `statusCode`, and `statusMessage`
> - Mutations to `response.headers` affect how the caching layer decides whether to cache the response and for how long
> - Header changes are applied to what gets cached, not to the response the user receives. The `statusCode` and `statusMessage` you set are also what the caller receives, because the caching layer records the status after this hook returns

**Note:**
> - This hook is only called when the `cache` option is enabled.

**Note:**
> - This hook must be synchronous. It cannot return a Promise. If you need async logic to determine caching behavior, use a `beforeRequest` hook instead.

**Note:**
> - When returning `false`, remaining hooks are skipped. The response headers the user receives are NOT modified - only the caching layer sees modified headers.

**Note:**
> - If a hook throws an error, it will be propagated and the request will fail. This is consistent with how other hooks in Got handle errors.

**Note:**
> - At this stage, the response body has not been read yet - it's still a stream. Properties like `response.body` and `response.rawBody` are not available. You can only inspect/modify response headers and status code.

```js
import got from 'got';

// Simple: Don't cache errors
const instance = got.extend({
	cache: new Map(),
	hooks: {
		beforeCache: [
			(response) => response.statusCode >= 400 ? false : undefined
		]
	}
});

await instance('https://example.com');
```

```js
import got from 'got';

// Advanced: Modify headers for fine control
const instance2 = got.extend({
	cache: new Map(),
	hooks: {
		beforeCache: [
			(response) => {
				// Force caching with explicit duration
				// Mutations work directly - no need to return
				response.headers['cache-control'] = 'public, max-age=3600';
			}
		]
	}
});
```

#### `afterResponse`

**Type: `AfterResponseHook[]`**\
**Default: `[]`**

```ts
(response: Response, retryWithMergedOptions: (options: OptionsInit) => never) => Promisable<Response | RequestPromise<Response>>
```

Each function should return the response. This is especially useful when you want to refresh an access token.

**Note:**
> - When using the Stream API, this hook is ignored.

**Note:**
> - Calling the `retryWithMergedOptions` function will trigger `beforeRetry` hooks. By default, remaining `afterResponse` hooks are removed to prevent duplicate execution. To preserve remaining hooks on retry, set `preserveHooks: true` in the options passed to `retryWithMergedOptions`. In case of an error, `beforeRetry` hooks will be called instead.
> - Meanwhile, the `init`, `beforeRequest`, and `beforeRedirect` hooks, as well as already executed `afterResponse` hooks, are skipped.

**Note:**
> - To preserve remaining `afterResponse` hooks after calling `retryWithMergedOptions`, set `preserveHooks: true` in the options passed to `retryWithMergedOptions`. This is useful when you want hooks to run on retried requests.

**Warning:**
> - Be cautious when using `preserveHooks: true`. If a hook unconditionally calls `retryWithMergedOptions` with `preserveHooks: true`, it will create an infinite retry loop. Always ensure hooks have proper conditional logic to avoid infinite retries.

```js
import got from 'got';

const instance = got.extend({
	hooks: {
		afterResponse: [
			(response, retryWithMergedOptions) => {
				// Unauthorized
				if (response.statusCode === 401) {
					// Refresh the access token
					const updatedOptions = {
						headers: {
							token: getNewToken()
						}
					};

					// Update the defaults
					instance.defaults.options.merge(updatedOptions);

					// Make a new retry
					return retryWithMergedOptions(updatedOptions);
				}

				// No changes otherwise
				return response;
			}
		],
		beforeRetry: [
			error => {
				// This will be called on `retryWithMergedOptions(...)`
			}
		]
	},
	mutableDefaults: true
});
```

**Example with `preserveHooks`:**

```js
import got from 'got';

const instance = got.extend({
	hooks: {
		afterResponse: [
			(response, retryWithMergedOptions) => {
				if (response.statusCode === 401) {
					return retryWithMergedOptions({
						headers: {
							authorization: getNewToken()
						},
						preserveHooks: true  // Keep remaining hooks
					});
				}

				return response;
			},
			(response) => {
				// This hook will run on the retried request
				// (the original request is interrupted when the first hook triggers a retry)
				console.log('Response received:', response.statusCode);
				return response;
			}
		]
	}
});
```

#### `beforeError`

**Type: `BeforeErrorHook[]`**\
**Default: `[]`**

```ts
(error: RequestError) => Promisable<Error>
```

Called with a [`RequestError`](8-errors.md#requesterror) instance. The error is passed to the hook right before it's thrown.

This hook can return any `Error` instance, allowing you to:
- Return custom error classes for better error handling in your application
- Extend `RequestError` with additional properties
- Return plain `Error` instances when you don't need Got-specific error information

This is especially useful when you want to have more detailed errors or maintain backward compatibility with existing error handling code.

```js
import got from 'got';

// Modify and return the error
await got('https://api.github.com/repos/sindresorhus/got/commits', {
	responseType: 'json',
	hooks: {
		beforeError: [
			error => {
				const {response} = error;
				if (response && response.body) {
					error.name = 'GitHubError';
					error.message = `${response.body.message} (${response.statusCode})`;
				}

				return error;
			}
		]
	}
});

// Return a custom error class
class CustomAPIError extends Error {
	constructor(message, statusCode) {
		super(message);
		this.name = 'CustomAPIError';
		this.statusCode = statusCode;
	}
}

await got('https://api.example.com/endpoint', {
	hooks: {
		beforeError: [
			error => {
				// Return a custom error for backward compatibility with your application
				return new CustomAPIError(
					error.message,
					error.response?.statusCode
				);
			}
		]
	}
});
```

---

### Documentation Async Stack Traces (`documentation/async-stack-traces.md`)

[> Back to homepage](../readme.md#documentation)

### Capturing async stack traces

**Caution:**
> - Capturing async stack traces can severely degrade performance!

**Want to skip the article? See the [Conclusion](#conclusion) where we discuss a mediocre solution.**

We live in a world full of bugs. Software is getting more and more complicated, which makes debugging increasingly more difficult. Ever had an error with no idea where it came from? Yeah, it's often not easy to track this down.

You might have noticed that the `.stack` of an error sometimes look incomplete. This is often due to the execution of an async function that is triggered by a timer. Following the example:

```js
await new Promise((resolve, reject) => {
	setTimeout(() => {
		reject(new Error('here'));
	});
});
```

```js
file:///home/szm/Desktop/got/demo.js:3
                reject(new Error('here'));
                       ^

Error: here
    at Timeout._onTimeout (file:///home/szm/Desktop/got/demo.js:3:10)
    at listOnTimeout (node:internal/timers:557:17)
    at processTimers (node:internal/timers:500:7)
```

The stack trace does not show where the timeout was set. It's currently not possible to determine this with the native `Promise`s. However, [`bluebird`](https://github.com/petkaantonov/bluebird/) exposes an option dedicated to capturing async stack traces:

```js
import Bluebird from 'bluebird';

Bluebird.config({longStackTraces: true});
global.Promise = Bluebird;

await new Promise((resolve, reject) => {
	setTimeout(() => {
		reject(new Error('here'));
	});
});
```

```text
node:internal/process/esm_loader:74
    internalBinding('errors').triggerUncaughtException(
                              ^

Error: here
    at Timeout._onTimeout (file:///home/szm/Desktop/got/demo.js:7:10)
    at listOnTimeout (node:internal/timers:557:17)
    at processTimers (node:internal/timers:500:7)
From previous event:
    at file:///home/szm/Desktop/got/demo.js:5:7
    at ModuleJob.run (node:internal/modules/esm/module_job:183:25)
    at async Loader.import (node:internal/modules/esm/loader:178:24)
    at async Object.loadESM (node:internal/process/esm_loader:68:5)
    at async handleMainPromise (node:internal/modules/run_main:63:12)
```

Now it's clear. We know that the timeout was set on line 5. Bluebird should be sufficient for Got:

```js
import Bluebird from 'bluebird';
import got from 'got';

Bluebird.config({longStackTraces: true});
global.Promise = Bluebird;

try {
	await got('https://httpbin.org/delay/1', {
		timeout: {
			request: 1
		},
		retry: {
			limit: 0
		}
	});
} catch (error) {
	console.error(error.stack);
}
```

```text
TimeoutError: Timeout awaiting 'request' for 1ms
    at ClientRequest.<anonymous> (file:///home/szm/Desktop/got/dist/source/core/index.js:780:61)
    at Object.onceWrapper (node:events:514:26)
    at ClientRequest.emit (node:events:406:35)
    at TLSSocket.socketErrorListener (node:_http_client:447:9)
    at TLSSocket.emit (node:events:394:28)
    at emitErrorNT (node:internal/streams/destroy:157:8)
    at emitErrorCloseNT (node:internal/streams/destroy:122:3)
    at processTicksAndRejections (node:internal/process/task_queues:83:21)
    at Timeout.timeoutHandler [as _onTimeout] (file:///home/szm/Desktop/got/dist/source/core/timed-out.js:42:25)
    at listOnTimeout (node:internal/timers:559:11)
    at processTimers (node:internal/timers:500:7)
From previous event:
    at new Promise (<anonymous>)
    at asPromise (file:///home/szm/Desktop/got/dist/source/as-promise/index.js:21:21)
    at lastHandler (file:///home/szm/Desktop/got/dist/source/create.js:42:27)
    at iterateHandlers (file:///home/szm/Desktop/got/dist/source/create.js:49:28)
    at got (file:///home/szm/Desktop/got/dist/source/create.js:69:16)
    at file:///home/szm/Desktop/got/demo.js:8:8
    at ModuleJob.run (node:internal/modules/esm/module_job:183:25)
    at async Loader.import (node:internal/modules/esm/loader:178:24)
    at async Object.loadESM (node:internal/process/esm_loader:68:5)
    at async handleMainPromise (node:internal/modules/run_main:63:12)
```

As expected, we know where the timeout has been set. Unfortunately, if we increase our retry count limit to `1`, the stack trace remains the same. That's because `bluebird` doesn't track I/O events. Please note that this should be sufficient for most cases. In order to debug further, we can use [`async_hooks`](https://nodejs.org/api/async_hooks.html) instead. A Stack Overflow user has come up with an awesome solution:

```js
import asyncHooks from 'node:async_hooks';

const traces = new Map();

asyncHooks.createHook({
	init(id) {
		const trace = {};
		Error.captureStackTrace(trace);
		traces.set(id, trace.stack.replace(/(^.+$\n){4}/m, '\n'));
	},
	destroy(id) {
		traces.delete(id);
	},
}).enable();

globalThis.Error = class extends Error {
	constructor(message) {
		super(message);
		this.constructor.captureStackTrace(this, this.constructor);
	}

	static captureStackTrace(what, where) {
		super.captureStackTrace.call(Error, what, where);

		const trace = traces.get(asyncHooks.executionAsyncId());
		if (trace) {
			what.stack += trace;
		}
	}
};
```

If we replace the `bluebird` part with this, we get:

```
/* Detailed source-code truncated for AI context efficiency. */
```

This is extremely long, and not a complete Node.js app. Just a demo. Imagine how long it would be if this was used with databases, file systems, etc.

#### Conclusion

All these workarounds have a large impact on performance. However, there is a possible solution to this madness. Got provides handlers, hooks, and context. We can capture the stack trace in a handler, store it in a context and expose it in a `beforeError` hook.

```js
import got from 'got';

const instance = got.extend({
	handlers: [
		(options, next) => {
			Error.captureStackTrace(options.context);
			return next(options);
		}
	],
	hooks: {
		beforeError: [
			error => {
				error.source = error.options.context.stack.split('\n');
				return error;
			}
		]
	}
});

try {
	await instance('https://httpbin.org/delay/1', {
		timeout: {
			request: 100
		},
		retry: {
			limit: 0
		}
	});
} catch (error) {
	console.error(error);
}
```

```
/* Detailed source-code truncated for AI context efficiency. */
```

Yay! This is much more readable. Furthermore, we capture the stack trace only when `got` is called. This is definitely going to have some performance impact, but it will be much more performant than the other mentioned solutions.

Curious to know more? Check out these links:
- https://stackoverflow.com/questions/54914770/is-there-a-good-way-to-surface-error-traces-in-production-across-event-emitters
- https://github.com/nodejs/node/issues/11370
- https://github.com/puppeteer/puppeteer/issues/2037
- https://github.com/nodejs/node/pull/13870

---

### Documentation Cache (`documentation/cache.md`)

[> Back to homepage](../readme.md#documentation)

## Cache

Got implements [RFC 7234](https://httpwg.org/specs/rfc7234.html) compliant HTTP caching which works out of the box in-memory and is easily pluggable with a wide range of storage adapters. Fresh cache entries are served directly from the cache, and stale cache entries are revalidated with `If-None-Match` / `If-Modified-Since` headers. You can read more about the underlying cache behavior in the [`cacheable-request` documentation](https://www.npmjs.com/package/cacheable-request).

`QUERY` requests are not cached by Got's built-in cache option. Correctly caching `QUERY` requires cache keys that include the request content.

Requests with streaming or iterable bodies, including native `FormData`, bypass the cache. Got does not consume an upload to generate a cache key.

You can use the JavaScript `Map` type as an in-memory cache:

```js
import got from 'got';

const map = new Map();

let response = await got('https://sindresorhus.com', {cache: map});
console.log(response.isFromCache);
//=> false

response = await got('https://sindresorhus.com', {cache: map});
console.log(response.isFromCache);
//=> true
```

Got uses [Keyv](https://github.com/lukechilds/keyv) internally to support a wide range of storage adapters. For something more scalable you could use an [official Keyv storage adapter](https://github.com/lukechilds/keyv#official-storage-adapters):

```sh
npm install @keyv/redis
```

```js
import got from 'got';
import KeyvRedis from '@keyv/redis';

const redis = new KeyvRedis('redis://user:pass@localhost:6379');

await got('https://sindresorhus.com', {cache: redis});
```

Got supports anything that follows the Map API, so it's easy to write your own storage adapter or use a third-party solution.

For example, the following are all valid storage adapters:

```js
const storageAdapter = new Map();

await got('https://sindresorhus.com', {cache: storageAdapter});
```

```js
import storageAdapter from './my-storage-adapter';

await got('https://sindresorhus.com', {cache: storageAdapter});
```

```js
import QuickLRU from 'quick-lru';

const storageAdapter = new QuickLRU({maxSize: 1000});

await got('https://sindresorhus.com', {cache: storageAdapter});
```

View the [Keyv docs](https://github.com/lukechilds/keyv) for more information on how to use storage adapters.

### Advanced caching mechanisms

The `request` function may return an instance of `IncomingMessage`-like class.

```js
import https from 'node:https';
import {Readable} from 'node:stream';
import got from 'got';

const getCachedResponse = (url, options) => {
	const response = new Readable({
		read() {
			this.push("Hello, world!");
			this.push(null);
		}
	});

	response.statusCode = 200;
	response.headers = {};
	response.trailers = {};
	response.socket = null;
	response.aborted = false;
	response.complete = true;
	response.httpVersion = '1.1';
	response.httpVersionMinor = 1;
	response.httpVersionMajor = 1;

	return response;
};

const instance = got.extend({
	request: (url, options, callback) => {
		return getCachedResponse(url, options);
	}
});

const body = await instance('https://example.com').text();

console.log(body);
//=> "Hello, world!"
```

If you don't want to alter the `request` function, you can return a cached response in a `beforeRequest` hook:

```js
import https from 'node:https';
import {Readable} from 'node:stream';
import got from 'got';

const getCachedResponse = (url, options) => {
	const response = new Readable({
		read() {
			this.push("Hello, world!");
			this.push(null);
		}
	});

	response.statusCode = 200;
	response.headers = {};
	response.trailers = {};
	response.socket = null;
	response.aborted = false;
	response.complete = true;
	response.httpVersion = '1.1';
	response.httpVersionMinor = 1;
	response.httpVersionMajor = 1;

	return response;
};

const instance = got.extend({
	hooks: {
		beforeRequest: [
			options => {
				return getCachedResponse(options.url, options);
			}
		]
	}
});

const body = await instance('https://example.com').text();

console.log(body);
//=> "Hello, world!"
```

If you want to prevent duplicating the same requests, you can use a handler instead.

```js
import got from 'got';

const map = new Map();

const instance = got.extend({
	handlers: [
		(options, next) => {
			if (options.isStream) {
				return next(options);
			}

			const pending = map.get(options.url.href);
			if (pending) {
				return pending;
			}

			const promise = next(options);

			map.set(options.url.href, promise);
			promise.finally(() => {
				map.delete(options.url.href);
			});

			return promise;
		}
	]
});

const [first, second] = await Promise.all([
	instance('https://httpbin.org/anything'),
	instance('https://httpbin.org/anything')
]);

console.log(first === second);
//=> true
```

---

### Documentation Diagnostics Channel (`documentation/diagnostics-channel.md`)

[> Back to homepage](../readme.md#documentation)

## Diagnostics Channel

Got integrates with Node.js [diagnostic channels](https://nodejs.org/api/diagnostics_channel.html) for low-overhead observability. Events are only published when subscribers exist.

### Channels

#### `got:request:create`

Emitted when a request is created.

```ts
{requestId: string, url: string, method: string}
```

#### `got:request:start`

Emitted before the native HTTP request is sent.

```ts
{requestId: string, url: string, method: string, headers: Record<string, string | string[] | undefined>}
```

#### `got:response:start`

Emitted when response headers are received.

```ts
{requestId: string, url: string, statusCode: number, headers: Record<string, string | string[] | undefined>, isFromCache: boolean}
```

#### `got:response:end`

Emitted when the response completes.

```ts
{requestId: string, url: string, statusCode: number, bodySize?: number, timings?: Timings}
```

#### `got:request:retry`

Emitted when retrying a request.

```ts
{requestId: string, retryCount: number, error: RequestError, delay: number}
```

#### `got:request:error`

Emitted when a request fails.

The error can be replaced by a `beforeError` hook. Use `error instanceof RequestError` before accessing Got-specific properties.

```ts
{requestId: string, url: string, error: Error, timings?: Timings}
```

#### `got:response:redirect`

Emitted when following a redirect.

```ts
{requestId: string, fromUrl: string, toUrl: string, statusCode: number}
```

### Example

```js
import diagnosticsChannel from 'node:diagnostics_channel';

const channel = diagnosticsChannel.channel('got:request:start');

channel.subscribe(message => {
	console.log(`${message.method} ${message.url}`);
});
```

All events for a single request share the same `requestId`.

### TypeScript

All message types are exported from the main package:

```ts
import type {
	DiagnosticRequestCreate,
	DiagnosticRequestStart,
	DiagnosticResponseStart,
	DiagnosticResponseEnd,
	DiagnosticRequestRetry,
	DiagnosticRequestError,
	DiagnosticResponseRedirect,
} from 'got';
```

---

### Documentation Lets Make A Plugin (`documentation/lets-make-a-plugin.md`)

[> Back to homepage](../readme.md#documentation)

## Let's make a plugin!

> Another example on how to use Got like a boss :electric_plug:

Okay, so you already have learned some basics. That's great!

When it comes to advanced usage, custom instances are really helpful.
For example, take a look at [`gh-got`](https://github.com/sindresorhus/gh-got).
It looks pretty complicated, but... it's simple and extremely useful.

Before we start, we need to find the [GitHub API docs](https://developer.github.com/v3/).

Let's write down the most important information:
1. The root endpoint is `https://api.github.com/`.
2. We will use version 3 of the API.\
   The `Accept` header needs to be set to `application/vnd.github.v3+json`.
3. The body is in a JSON format.
4. We will use OAuth2 for authorization.
5. We may receive `400 Bad Request` or `422 Unprocessable Entity`.\
   The body contains detailed information about the error.
6. *Pagination?* Yeah! Supported natively by Got.
7. Rate limiting. These headers are interesting:

- `X-RateLimit-Limit`
- `X-RateLimit-Remaining`
- `X-RateLimit-Reset`

Also `X-GitHub-Request-Id` may be useful for debugging.

8. The `User-Agent` header is required.

When we have all the necessary info, we can start mixing :cake:

### The root endpoint

Not much to do here. Just extend an instance and provide the `prefixUrl` option:

```js
import got from 'got';

const instance = got.extend({
	prefixUrl: 'https://api.github.com'
});

export default instance;
```

### v3 API

GitHub needs to know which API version we are using. We'll use the `Accept` header for that:

```js
import got from 'got';

const instance = got.extend({
	prefixUrl: 'https://api.github.com',
	headers: {
		accept: 'application/vnd.github.v3+json'
	}
});

export default instance;
```

### JSON body

We'll use [`options.responseType`](2-options.md#responsetype):

```js
import got from 'got';

const instance = got.extend({
	prefixUrl: 'https://api.github.com',
	headers: {
		accept: 'application/vnd.github.v3+json'
	},
	responseType: 'json'
});

export default instance;
```

### Authorization

It's common to set some environment variables, for example, `GITHUB_TOKEN`. You can modify the tokens in all your apps easily, right? Cool. What about... we want to provide a unique token for each app. Then we will need to create a new option - it will default to the environment variable, but you can easily override it.

Got performs option validation and doesn't know that `token` is a wanted option so it will throw. We can handle it inside an `init` hook and save it in `options.context`.

```js
import got from 'got';

const instance = got.extend({
	prefixUrl: 'https://api.github.com',
	headers: {
		accept: 'application/vnd.github.v3+json'
	},
	responseType: 'json',
	context: {
		token: process.env.GITHUB_TOKEN,
	},
	hooks: {
		init: [
			(raw, options) => {
				if ('token' in raw) {
					options.context.token = raw.token;
					delete raw.token;
				}
			}
		]
	}
});

export default instance;
```

For the rest we will use a handler. We could use hooks, but this way it will be more readable. Having `beforeRequest`, `beforeError` and `afterResponse` hooks for just a few lines of code would complicate things unnecessarily.

**Tip:**
> - It's a good practice to use hooks when your plugin gets complicated.
> - Try not to overload the handler function, but don't abuse hooks either.

```js
import got from 'got';

const instance = got.extend({
	prefixUrl: 'https://api.github.com',
	headers: {
		accept: 'application/vnd.github.v3+json'
	},
	responseType: 'json',
	context: {
		token: process.env.GITHUB_TOKEN,
	},
	hooks: {
		init: [
			(raw, options) => {
				if ('token' in raw) {
					options.context.token = raw.token;
					delete raw.token;
				}
			}
		]
	},
	handlers: [
		(options, next) => {
			// Authorization
			const {token} = options.context;
			if (token && !options.headers.authorization) {
				options.headers.authorization = `token ${token}`;
			}

			return next(options);
		}
	]
});

export default instance;
```

### Errors

We should name our errors, just to know if the error is from the API response. Superb errors, here we come!

```js
...
	handlers: [
		(options, next) => {
			// Authorization
			const {token} = options.context;
			if (token && !options.headers.authorization) {
				options.headers.authorization = `token ${token}`;
			}

			// Don't touch streams
			if (options.isStream) {
				return next(options);
			}

			// Magic begins
			return (async () => {
				try {
					const response = await next(options);

					return response;
				} catch (error) {
					const {response} = error;

					// Nicer errors
					if (response && response.body) {
						error.name = 'GitHubError';
						error.message = `${response.body.message} (${response.statusCode} status code)`;
					}

					throw error;
				}
			})();
		}
	]
...
```

Note that by providing our own errors in handlers, we don't alter the ones in `beforeError` hooks.\
The conversion is the last thing here.

### Rate limiting

Umm... `response.headers['x-ratelimit-remaining']` doesn't look good. What about `response.rateLimit.limit` instead?\
Yeah, definitely. Since `response.headers` is an object, we can easily parse these:

```js
const getRateLimit = (headers) => ({
	limit: Number.parseInt(headers['x-ratelimit-limit'], 10),
	remaining: Number.parseInt(headers['x-ratelimit-remaining'], 10),
	reset: new Date(Number.parseInt(headers['x-ratelimit-reset'], 10) * 1000)
});

getRateLimit({
	'x-ratelimit-limit': '60',
	'x-ratelimit-remaining': '55',
	'x-ratelimit-reset': '1562852139'
});
// => {
// 	limit: 60,
// 	remaining: 55,
// 	reset: 2019-07-11T13:35:39.000Z
// }
```

Let's integrate it:

```js
const getRateLimit = (headers) => ({
	limit: Number.parseInt(headers['x-ratelimit-limit'], 10),
	remaining: Number.parseInt(headers['x-ratelimit-remaining'], 10),
	reset: new Date(Number.parseInt(headers['x-ratelimit-reset'], 10) * 1000)
});

...
	handlers: [
		(options, next) => {
			// Authorization
			const {token} = options.context;
			if (token && !options.headers.authorization) {
				options.headers.authorization = `token ${token}`;
			}

			// Don't touch streams
			if (options.isStream) {
				return next(options);
			}

			// Magic begins
			return (async () => {
				try {
					const response = await next(options);

					// Rate limit for the Response object
					response.rateLimit = getRateLimit(response.headers);

					return response;
				} catch (error) {
					const {response} = error;

					// Nicer errors
					if (response && response.body) {
						error.name = 'GitHubError';
						error.message = `${response.body.message} (${response.statusCode} status code)`;
					}

					// Rate limit for errors
					if (response) {
						error.rateLimit = getRateLimit(response.headers);
					}

					throw error;
				}
			})();
		}
	]
...
```

### The frosting on the cake: `User-Agent` header.

```js
const packageJson = {
	name: 'gh-got',
	version: '12.0.0'
};

const instance = got.extend({
	...
	headers: {
		accept: 'application/vnd.github.v3+json',
		'user-agent': `${packageJson.name}/${packageJson.version}`
	},
	...
});
```

## Woah. Is that it?

Yup. View the full source code [here](examples/gh-got.js). Here's an example of how to use it:

```js
import ghGot from 'gh-got';

const response = await ghGot('users/sindresorhus');
const creationDate = new Date(response.created_at);

console.log(`Sindre's GitHub profile was created on ${creationDate.toGMTString()}`);
// => Sindre's GitHub profile was created on Sun, 20 Dec 2009 22:57:02 GMT
```

### Pagination

```js
import ghGot from 'gh-got';

const countLimit = 50;
const pagination = ghGot.paginate(
	'repos/sindresorhus/got/commits',
	{
		pagination: {countLimit}
	}
);

console.log(`Printing latest ${countLimit} Got commits (newest to oldest):`);

for await (const commitData of pagination) {
	console.log(commitData.commit.message);
}
```

That's... astonishing! We don't have to implement pagination on our own. Got handles it all.

### At the end

Did you know you can mix many instances into a bigger, more powerful one? Check out the [Advanced Creation](examples/advanced-creation.js) guide.

---

### Documentation Quick Start (`documentation/quick-start.md`)

# Quick Start Guide

## Getting and posting data with promises

The simplest `GET` request:

```js
import got from 'got';

const url = 'https://httpbin.org/anything';
const response = await got(url);
```

The call returns a <code>Promise<[Response](3-streams.md#response-2)></code>. If the body contains JSON, it can be retrieved directly:

```js
import got from 'got';

const url = 'https://httpbin.org/anything';
const data = await got(url).json();
```

The similar <code>[got.text](1-promise.md#promisetext)</code> method returns plain text.

All `got` methods accept an options object for passing extra configuration, such as headers:

```js
import got from 'got';

const url = 'https://httpbin.org/anything';

const options = {
	headers: {
		'Custom-Header': 'Quick start',
	},
	timeout: {
		send: 3500
	},
};

const data = await got(url, options).json();
```

A `POST` request is very similar:

```js
import got from 'got';

const url = 'https://httpbin.org/anything';

const options = {
	json: {
		documentName: 'Quick Start',
	},
};

const data = await got.post(url, options);
```

The request body is passed in the options object. The `json` property will automatically set headers accordingly. Custom headers can be added exactly as above.

## Using streams

The [Stream API](3-streams.md) allows to leverage [Node.js Streams](https://nodejs.dev/learn/nodejs-streams) capabilities:

```js
import fs from 'node:fs';
import {pipeline as streamPipeline} from 'node:stream/promises';
import got from 'got';

const url = 'https://httpbin.org/anything';

const options = {
	json: {
		documentName: 'Quick Start',
	},
};

const gotStream = got.stream.post(url, options);

const outStream = fs.createWriteStream('anything.json');

try {
	await streamPipeline(gotStream, outStream);
} catch (error) {
	console.error(error);
}
```

## Options

Options can be set at the client level and reused in subsequent queries:

```js
import got from 'got';

const options = {
	prefixUrl: 'https://httpbin.org',
	headers: {
		Authorization: getTokenFromVault(),
	},
};

const client = got.extend(options);

export default client;
```

Some noticeable common options are:
- [`searchParams`](2-options.md#searchparams): A query string object.
- [`prefixUrl`](2-options.md#prefixurl): Prepended to query paths. Paths must be relative to prefix, i.e. not begin with a `/`.
- [`method`](2-options.md#method): The HTTP method name.
- [`headers`](2-options.md#headers): Query headers.
- [`json`](2-options.md#json): JSON body.
- [`form`](2-options.md#form): A form query string object.

See the documentation for other [options](2-options.md#options).

## Errors

Both Promise and Stream APIs throw errors with metadata.

```js
import got from 'got';

try {
	const data = await got.get('https://httpbin.org/status/404');
} catch (error) {
	console.error(error.response.statusCode);
}
```

```js
import got from 'got';

const stream = got.stream
	.get('https://httpbin.org/status/404')
	.once('error', error => {
		console.error(error.response.statusCode);
	});
```

## Miscellaneous

The HTTP method name can also be given as an option, this may be convenient when it is known only at runtime:

```js
import got from 'got';

const url = 'https://httpbin.org/anything';

const method = 'POST';

const options = {
	method,
	json: {
		documentName: 'Quick Start',
	},
};

const data = await got(url, options);
```

For most apps, HTTP clients just do `GET` and `POST` queries (`PUT`, `PATCH` or `DELETE` methods work similarly).
The following sections will give some pointers to more advanced usage.

### Timeouts

By default, requests have no timeout. It is a good practice to set one:

```js
import got from 'got';

const options = {
	timeout: {
		request: 10000,
	},
};

const client = got.extend(options);

export default client;
```

The above sets a global timeout of 10000 milliseconds for all requests issued by the exported `client`. Like all options, timeouts can also be set at the request level. See the [`timeout` option](6-timeout.md#timeout-options).

### Retries

A failed request is retried twice. The retry policy may be tuned with a [`retry`](7-retry.md#retry) options object.

```js
import got from 'got';

const options = {
	retry: {
		limit: 5,
		errorCodes: [
			'ETIMEDOUT'
		],
	},
};
```

Retries with stream are a little trickier, see [`stream.on('retry', …)`](3-streams.md#streamonretry-).

### Hooks

Hooks are custom functions called on some request events:

```js
import got from 'got';

const logRetry = (error, retryCount) => {
	console.error(`Retrying after error ${error.code}, retry #: ${retryCount}`);
};

const options = {
	hooks: {
		beforeRetry: [
			logRetry,
		],
	},
};

const client = got.extend(options);

export default client;
```

*Note that hooks are given as arrays*, thus multiple hooks can be given. See documentation for other possible [hooks](9-hooks.md#hooks-api).

### Going further

There is a lot more to discover in the [documentation](../readme.md#documentation) and [tips](tips.md#tips). Among others, `Got` can handle [cookies](tips.md#cookies), [pagination](4-pagination.md#pagination-api), [cache](cache.md#cache). Please read the documentation before implementing something that is already done by `Got` :innocent:.

---

### Documentation Tips (`documentation/tips.md`)

[> Back to homepage](../readme.md#documentation)

## Tips

### Timeout

Each request can have a maximum allowed time to run.\
In order to use this, specify the `request` timeout option.

```js
import got from 'got';

const body = await got('https://httpbin.org/anything', {
	timeout: {
		request: 30000
	}
});
```

For more specific timeouts, visit the [Timeout API](6-timeout.md).

### Retries

By default, Got makes a new retry on a failed request if possible.

It is possible to disable this feature entirely by setting the amount of maximum allowed retries to `0`.

```js
import got from 'got';

const noRetryGot = got.extend({
	retry: {
		limit: 0
	}
});
```

In order to specify retriable errors, use the [Retry API](7-retry.md).

### Cookies

Got supports cookies out of box. There is no need to parse them manually.\
In order to use cookies, pass a `CookieJar` instance from the [`tough-cookie`](https://github.com/salesforce/tough-cookie) package.

```js
import got from 'got';
import {CookieJar} from 'tough-cookie';

const cookieJar = new CookieJar();

await cookieJar.setCookie('foo=bar', 'https://httpbin.org');
await got('https://httpbin.org/anything', {cookieJar});
```

### Sessions

You can create a session by extending Got with a shared `CookieJar`. This automatically saves and reuses cookies between requests:

```js
import got from 'got';
import {CookieJar} from 'tough-cookie';

const cookieJar = new CookieJar();
const sessionGot = got.extend({cookieJar});

// The cookie is saved within the cookie jar of the session
await sessionGot('http://httpbin.org/cookies/set?my_cookie=my_value');

await sessionGot('http://httpbin.org/cookies', {responseType: 'json', resolveBodyOnly: true});
//=> {cookies: {my_cookie: 'my_value'}}

// The cookies only apply to the session
await got('http://httpbin.org/cookies', {responseType: 'json', resolveBodyOnly: true});
//=> {cookies: {}}
```

This also works with caching and other Got features.

### AWS

Requests to AWS services need to have their headers signed.\
This can be accomplished by using the [`got4aws`](https://github.com/SamVerschueren/got4aws) package.

This is an example for querying an [`API Gateway`](https://docs.aws.amazon.com/apigateway/api-reference/signing-requests/) with a signed request.

```js
import got4aws from 'got4aws';

const got = got4aws();

const response = await got('https://<api-id>.execute-api.<api-region>.amazonaws.com/<stage>/endpoint/path', {
	// …
});
```

### Pagination

When working with large datasets, it's very efficient to use pagination.\
By default, Got uses the [`Link` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link) to retrieve the next page.\
However, this behavior can be customized, see the [Pagination API](4-pagination.md).

```js
const countLimit = 50;

const pagination = got.paginate('https://api.github.com/repos/sindresorhus/got/commits', {
	pagination: {countLimit}
});

console.log(`Printing latest ${countLimit} Got commits (newest to oldest):`);

for await (const commitData of pagination) {
	console.log(commitData.commit.message);
}
```

### Measuring traffic usage

You can measure the total bytes transferred (sent and received) for a request by accessing the underlying socket's `bytesWritten` and `bytesRead` properties.\
The socket is available via [`stream.socket`](3-streams.md#streamsocket) or [`response.request.socket`](3-streams.md#socket).

**Note:**
> - The socket's byte counters include HTTP headers and all protocol overhead, not just the request/response body.
> - The socket may be `undefined` when using cached responses.

```js
import got from 'got';

const stream = got.stream('https://httpbin.org/json');

stream.on('response', () => {
	console.log(`Downloaded: ${stream.socket.bytesRead} bytes`);
	console.log(`Uploaded: ${stream.socket.bytesWritten} bytes`);
});

stream.on('end', () => {
	const totalTraffic = stream.socket.bytesRead + stream.socket.bytesWritten;
	console.log(`Total traffic: ${totalTraffic} bytes`);
});

stream.resume();
```

You can also access the socket when using the Promise API:

```js
import got from 'got';

const request = got('https://httpbin.org/json');

request.on('response', response => {
	console.log(`Downloaded: ${response.request.socket.bytesRead} bytes`);
	console.log(`Uploaded: ${response.request.socket.bytesWritten} bytes`);
});

const response = await request;

if (response.request.socket) {
	const totalTraffic = response.request.socket.bytesRead + response.request.socket.bytesWritten;
	console.log(`Total traffic: ${totalTraffic} bytes`);
}
```

### Rate limiting (throttling transfer speed)

Got doesn't include built-in bandwidth throttling, but you can implement it using stream transformers.

> [!NOTE]
> - To limit total transfer **size** (not rate), see the [advanced-creation example](examples/advanced-creation.js).
> - For production use, consider something like  [`bandwidth-throttle-stream`](https://github.com/patrickkunka/bandwidth-throttle-stream).

```js
import got from 'got';
import {Transform} from 'node:stream';
import {setTimeout} from 'node:timers/promises';

class ThrottleTransform extends Transform {
	constructor(bytesPerSecond) {
		super();
		this.bytesPerSecond = bytesPerSecond;
	}

	async _transform(chunk, encoding, callback) {
		const delay = (chunk.length / this.bytesPerSecond) * 1000;
		if (delay > 0) {
			await setTimeout(delay);
		}

		callback(null, chunk);
	}
}

// Throttle download
got.stream('https://httpbin.org/bytes/1048576')
	.pipe(new ThrottleTransform(100000)) // 100 KB/s
	.pipe(process.stdout);
```

For uploads, throttle the body stream before passing to Got:

```js
import {createReadStream} from 'node:fs';

await got.post('https://httpbin.org/post', {
	body: createReadStream('file.bin').pipe(new ThrottleTransform(100000))
});
```

<a name="unix"></a>
### UNIX Domain Sockets

See the [`enableUnixSockets` option](./2-options.md#enableunixsockets).

### Testing

Got uses the native [`http`](https://nodejs.org/api/http.html) module, which depends on the native [`net`](https://nodejs.org/api/net.html) module.\
This means there are two possible ways to test:

1. Use a mocking library like [`nock`](https://github.com/nock/nock),
2. Create a server.

The first approach should cover all common use cases.\
Bear in mind that it overrides the native `http` module, so bugs may occur due to the differences.

The most solid way is to create a server.\
There may be cases where `nock` won't be sufficient or lacks functionality.

#### Nock

By default `nock` mocks only one request.\
Got will [retry](7-retry.md) on failed requests by default, causing a `No match for request ...` error.\
The solution is to either disable retrying (set `options.retry.limit` to `0`) or call `.persist()` on the mocked request.

```js
import got from 'got';
import nock from 'nock';

const scope = nock('https://sindresorhus.com')
	.get('/')
	.reply(500, 'Internal server error')
	.persist();

try {
	await got('https://sindresorhus.com')
} catch (error) {
	console.log(error.response.body);
	//=> 'Internal server error'

	console.log(error.response.retryCount);
	//=> 2
}

scope.persist(false);
```

### Proxying

**Note:**
> - The popular [`tunnel`](https://www.npmjs.com/package/tunnel) package is unmaintained. Use at your own risk.
> - The [`proxy-agent`](https://www.npmjs.com/package/proxy-agent) family doesn't follow newest Node.js features and lacks support.

Although there isn't a perfect, bug-free package, [Apify](https://apify.com/)'s solution is a modern one.\
See [`got-scraping/src/agent/h1-proxy-agent.ts`](https://github.com/apify/got-scraping/blob/2ec7f9148917a6a38d6d1c8c695606767c46cce5/src/agent/h1-proxy-agent.ts). It has the same API as `hpagent`.

[`hpagent`](https://github.com/delvedor/hpagent) is a modern package as well. In contrast to `tunnel`, it allows keeping the internal sockets alive to be reused.

```js
import got from 'got';
import {HttpsProxyAgent} from 'hpagent';

await got('https://sindresorhus.com', {
	agent: {
		https: new HttpsProxyAgent({
			keepAlive: true,
			keepAliveMsecs: 1000,
			maxSockets: 256,
			maxFreeSockets: 256,
			scheduling: 'lifo',
			proxy: 'https://localhost:8080'
		})
	}
});
```

Alternatively, use [`global-agent`](https://github.com/gajus/global-agent) to configure a global proxy for all HTTP/HTTPS traffic in your program.

Got's built-in HTTP/2 client does not include proxy support.

### Retry without an agent

If you're using proxies, you may run into connection issues.\
One way out is to disable proxies when retrying. The solution for the Stream API looks like this:

```js
import https from 'node:https';
import fs from 'node:fs';
import got from 'got';

class MyAgent extends https.Agent {
	createConnection(port, options, callback) {
		console.log(`Connecting with MyAgent`);
		return https.Agent.prototype.createConnection.call(this, port, options, callback);
	}
}

const proxy = new MyAgent();

let writeStream;

const fn = retryStream => {
	const options = {
		agent: {
			https: proxy,
		}
	};

	const stream = retryStream ?? got.stream('https://example.com', options);

	if (writeStream) {
		writeStream.destroy();
	}

	writeStream = fs.createWriteStream('example-com.html');

	stream.pipe(writeStream);
	stream.once('retry', (retryCount, error, createRetryStream) => {
		fn(createRetryStream({
			agent: {
				http: undefined,
				https: undefined,
				http2: undefined,
			},
		}));
	});
};

fn();
```

### `h2c`

There is no direct [`h2c` Upgrade](https://datatracker.ietf.org/doc/html/rfc9113#section-11.2) support.

However, you can provide a `h2session` option in a `beforeRequest` hook. See [an example](examples/h2c.js).

### Uppercase headers

Got always normalizes the headers, therefore passing an `Uppercase-Header` will transform it into `uppercase-header`. To fix this, you need to pass a wrapped agent:

```js
class WrappedAgent {
    constructor(agent) {
        this.agent = agent;
    }

    addRequest(request, options) {
        return this.agent.addRequest(request, options);
    }

    get keepAlive() {
        return this.agent.keepAlive;
    }

    get maxSockets() {
        return this.agent.maxSockets;
    }

    get options() {
        return this.agent.options;
    }

    get defaultPort() {
        return this.agent.defaultPort;
    }

    get protocol() {
        return this.agent.protocol;
    }
}

class TransformHeadersAgent extends WrappedAgent {
    addRequest(request, options) {
        const headers = request.getHeaderNames();

        for (const header of headers) {
            request.setHeader(this.transformHeader(header), request.getHeader(header));
        }

        return super.addRequest(request, options);
    }

    transformHeader(header) {
        return header.split('-').map(part => {
            return part[0].toUpperCase() + part.slice(1);
        }).join('-');
    }
}

const agent = new http.Agent({
    keepAlive: true
});

const wrappedAgent = new TransformHeadersAgent(agent);
```

See [an example](examples/uppercase-headers.js).

### Custom options

Got v12 throws when an option does not exist. Therefore passing a top-level option such as:

```js
import got from 'got';

await got('https://example.com', {
	foo: 'bar'
});
```

will throw. To prevent this, you need read the option in an `init` hook:

```js
import got from 'got';

const convertFoo = got.extend({
	hooks: {
		init: [
			(rawOptions, options) => {
				if ('foo' in rawOptions) {
					options.context.foo = rawOptions.foo;
					delete rawOptions.foo;
				}
			}
		]
	}
});

const instance = got.extend(convertFoo, {
	hooks: {
		beforeRequest: [
			options => {
				options.headers.foo = options.context.foo;
			}
		]
	}
});

const {headers} = await instance('https://httpbin.org/anything', {foo: 'bar'}).json();
console.log(headers.Foo); //=> 'bar'
```

Eventually, you may want to create a catch-all instance:

```js
import got from 'got';

const catchAllOptions = got.extend({
    hooks: {
        init: [
            (raw, options) => {
                for (const key in raw) {
                    if (!(key in options)) {
                        options.context[key] = raw[key];
                        delete raw[key];
                    }
                }
            }
        ]
    }
});

const instance = got.extend(catchAllOptions, {
	hooks: {
		beforeRequest: [
			options => {
				// All custom options will be visible under `options.context`
				options.headers.foo = options.context.foo;
			}
		]
	}
});

const {headers} = await instance('https://httpbin.org/anything', {foo: 'bar'}).json();
console.log(headers.Foo); //=> 'bar'
```

**Note:**
> - It's a good practice to perform the validation inside the `init` hook. You can safely throw when an option is unknown! Internally, Got uses the [`@sindresorhus/is`](https://github.com/sindresorhus/is) package.

### Electron `net` module is not supported

**Note:** Got v12 and later is an ESM package, but Electron does not yet support ESM. So you need to use Got v11.

Got doesn't support the `electron.net` module. It's missing crucial APIs that are available in Node.js.\
While Got used to support `electron.net`, it got very unstable and caused many errors.

However, you can use [IPC communication](https://www.electronjs.org/docs/api/ipc-main#ipcmainhandlechannel-listener) to get the Response object:

```js
// Main process
const got = require('got');

const instance = got.extend({
	// ...
});

ipcMain.handle('got', async (event, ...args) => {
	const {statusCode, headers, body} = await instance(...args);
	return {statusCode, headers, body};
});

// Renderer process
async () => {
	const {statusCode, headers, body} = await ipcRenderer.invoke('got', 'https://httpbin.org/anything');
	// ...
}
```

---

### Documentation Typescript (`documentation/typescript.md`)

[> Back to homepage](../readme.md#documentation)

## TypeScript

Got is fully written in TypeScript, so the integration is seamless.\
Furthermore, types have saved Got from many bugs and inconsistencies.

### Wrapping Got in an API client

You can wrap Got in a type-safe API client while preserving all Got features including progress/events support:

```typescript
import got, {type RequestPromise, type OptionsOfJSONResponseBody, type Progress} from 'got';

interface FizzBuzz {
	fizz: boolean;
	buzz: boolean;
}

class ExampleAPI {
	private client = got.extend({
		prefixUrl: 'https://api.example.com'
	});

	isFizzBuzz(number: number): RequestPromise<FizzBuzz> {
		return this.client.get('fizzbuzz', {
			searchParams: {number},
			responseType: 'json',
			resolveBodyOnly: true
		}).json<FizzBuzz>();
	}

	// For more complex cases, you can specify options
	getData(id: string, options?: OptionsOfJSONResponseBody): RequestPromise<FizzBuzz> {
		return this.client.get(`data/${id}`, {
			...options,
			responseType: 'json',
			resolveBodyOnly: true
		}).json<FizzBuzz>();
	}
}

const api = new ExampleAPI();

// The promise has full type support
const promise = api.isFizzBuzz(20);

// Event methods are available
promise.on('downloadProgress', (progress: Progress) => {
	console.log(progress.percent);
});

// The result is properly typed
const result = await promise;
console.log(result.fizz); // boolean
console.log(result.buzz); // boolean
```

**Note:**
> - Use `.json<T>()` to get `RequestPromise<T>` with the body-only response.
> - Use `.buffer()` or `.text()` for other response types.
> - Without calling these methods, you'll get `RequestPromise<Response<T>>`.
> - Use `signal` and `AbortController` to abort requests.

### Types

Here's a list of types that Got exports:

**Note:**
> - The list may be incomplete. If you find a type is missing, please open an issue about it.

### [`Got`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L200)

### [`GotStream`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L195)

### [`GotRequestFunction`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L142)

### [`GotPaginate`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L87)

### [`OptionsWithPagination<T = unknown, R = unknown>`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L82)

### [`OptionsOfTextResponseBody`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L74)

### [`OptionsOfJSONResponseBody`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L75)

### [`OptionsOfBufferResponseBody`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L76)

### [`StrictOptions`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L78)

### [`StreamOptions`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L79)

### [`OptionsInit`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/core/options.ts#L535)

### [`ExtendOptions`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L55)

### [`PlainResponse`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/core/response.ts#L6)

### [`Response<T = unknown>`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/core/response.ts#L95)

### [`Request`](https://github.com/sindresorhus/got/blob/ecb05343dea3bd35933585a1ec5bcea01348d109/source/core/index.ts#L139)

### [`RequestEvents<T>`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/core/index.ts#L108)

### [`Progress`](https://github.com/sindresorhus/got/blob/0f9f2b83b77710f2dc08c2a6bce1c78ba8d46760/source/core/index.ts#L40)

### [`InstanceDefaults`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L17)

### [`GotReturn`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L44)

### [`HandlerFunction`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/types.ts#L50)

### [`RequestPromise<T extends Response | Response['body'] = Response['body']>`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/as-promise/types.ts#L26)

### [`Delays`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/core/timed-out.ts#L14)

### [`Timings`](https://github.com/sindresorhus/got/blob/main/source/core/utils/timer.ts#L19)

### [`CreateConnectionFunction`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/core/options.ts#L302)

### [`CheckServerIdentityFunction`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/core/options.ts#L303)

### [`CacheOptions`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/core/options.ts#L305)

### [`HttpsOptions`](https://github.com/sindresorhus/got/blob/ae04c0e36cf3f5b2e356df7d48a5b19988f935a2/source/core/options.ts#L319)

### [`PaginateData<BodyType, ElementType>`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L382)

### [`PaginationOptions<ElementType, BodyType>`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L397)

### [`SearchParameters`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L504)

### [`ResponseType`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L518)

### [`FilterData<ElementType>`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L388)

### [`RetryObject`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L259)

### [`RetryFunction`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L267)

### [`ParseJsonFunction`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L235)

### [`StringifyJsonFunction`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L236)

### [`Method`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L241)

### [`ToughCookieJar`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L53)

### [`PromiseCookieJar`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L60)

### [`DnsLookupIpVersion`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L35)

### [`RequestFunction`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L43)

### [`Agents`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L45)

### [`Headers`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L51)

### [`Hooks`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L75)

### [`InitHook`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L65)

### [`BeforeRequestHook`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L66)

### [`BeforeRedirectHook`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L67)

### [`BeforeErrorHook`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L68)

### [`BeforeRetryHook`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L69)

### [`AfterResponseHook`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L70)

---

### Documentation Migration Guides Axios (`documentation/migration-guides/axios.md`)

[> Back to homepage](../../readme.md#documentation)

## Migration guides

> You may think it's too hard to switch, but it's really not. 🦄

### Axios

Axios is very similar to Got. The difference is that Axios targets browsers first, while Got fully makes use of Node.js features.

#### Common options

These options remain the same as well:

- [`url`](../2-options.md#url)
- [`method`](../2-options.md#method)
- [`headers`](../2-options.md#headers)
- [`maxRedirects`](../2-options.md#maxredirects)
- [`decompress`](../2-options.md#decompress)

#### Renamed options

We deeply care about readability, so we renamed these options:

- `httpAgent` → [`agent.http`](../2-options.md#agent)
- `httpsAgent` → [`agent.https`](../2-options.md#agent)
- `socketPath` → [`url`](../2-options.md#enableunixsockets)
- `responseEncoding` → [`encoding`](../2-options.md#encoding)
- `auth.username` → [`username`](../2-options.md#username)
- `auth.password` → [`password`](../2-options.md#password)
- `data` → [`body`](../2-options.md#body) / [`json`](../2-options.md#json) / [`form`](../2-options.md#form)
- `params` → [`searchParams`](../2-options.md#searchparams)

#### Changes in behavior

- `transformRequest` → [`hooks.beforeRequest`](../9-hooks.md#beforerequest)
  - The API is different.
- `transformResponse` → [`hooks.afterResponse`](../9-hooks.md#afterresponse)
  - The API is different.
- `baseUrl` → [`prefixUrl`](../2-options.md#prefixurl)
  - The `prefixUrl` is prepended to relative string input. Absolute URLs bypass `prefixUrl` by default. Use [`allowAbsoluteUrls: false`](../2-options.md#allowabsoluteurls) to reject absolute URLs when `prefixUrl` is set.
- [`timeout`](../6-timeout.md)
  - This option is now an object. You can now set timeouts on particular events!
- [`responseType`](../2-options.md#responsetype)
  - Accepts `'text'`, `'json'` or `'buffer'`.

#### Breaking changes

- `onUploadProgress`
  - This option does not exist. Instead, use [`got(…).on('uploadProgress', …)`](../3-streams.md#uploadprogress).
- `onDownloadProgress`
  - This option does not exist. Instead, use [`got(…).on('downloadProgress', …)`](../3-streams.md#downloadprogress).
- `maxContentLength`
  - This option does not exist. Instead, use [a handler](../examples/advanced-creation.js).
- `validateStatus`
  - This option does not exist. Got automatically validates the status according to [the specification](https://datatracker.ietf.org/doc/html/rfc7231#section-6).
- `proxy`
  - This option does not exist. You need to pass [an `agent`](../tips.md#proxy) instead.
- `cancelToken`
  - Use the [`signal`](../2-options.md#signal) option with [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController).
- `paramsSerializer`
  - This option does not exist.
- `maxBodyLength`
  - This option does not exist.

#### Response

The response object is different as well:

- `response.data` → [`response.body`](../3-streams.md#response-2)
- `response.status` → [`response.statusCode`](../3-streams.md#response-2)
- `response.statusText` → [`response.statusMessage`](../3-streams.md#response-2)
- `response.config` → [`response.request.options`](../3-streams.md#response-2)
- [`response.request`](../3-streams.md#response-2)
  - Returns [a Got stream](../3-streams.md).

The `response.headers` object remains the same.

#### Interceptors

Got offers [hooks](../9-hooks.md) instead, which are more flexible.

#### Errors

Errors look the same, with the difference `error.request` returns a Got stream. Furthermore, Got provides [more details](../8-errors.md) to make debugging easier.

#### Abort

Got supports [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) via the [`signal`](../2-options.md#signal) option.

#### Convenience methods

Convenience methods, such as `axios.get(…)` etc. remain the same: `got.get(…)`. Instead of `axios.create(…)` use `got.extend(…)`.

#### You're good to go!

Well, you have already come this far :tada:\
Take a look at the [documentation](../../readme.md#documentation). It's worth the time to read it.\
There are [some great tips](../tips.md).

If something is unclear or doesn't work as it should, don't hesitate to [open an issue](https://github.com/sindresorhus/got/issues/new/choose).

---

### Documentation Migration Guides Nodejs (`documentation/migration-guides/nodejs.md`)

[> Back to homepage](../../readme.md#documentation)

## Migration guides

> You may think it's too hard to switch, but it's really not. 🦄

### Node.js

Let's make a simple request. With Node.js, this is:

```js
import http from 'node:http';

const request = http.request('https://httpbin.org/anything', response => {
	if (response.statusCode >= 400) {
		request.destroy(new Error());
		return;
	}

	const chunks = [];
	let totalLength = 0;

	response.on('data', chunk => {
		chunks.push(chunk);
		totalLength += chunk.length;
	});

	response.once('end', () => {
		const bytes = new Uint8Array(totalLength);
		let offset = 0;
		for (const chunk of chunks) {
			bytes.set(chunk, offset);
			offset += chunk.length;
		}

		if (response.statusCode >= 400) {
			const error = new Error(`Unsuccessful response: ${response.statusCode}`);
			error.body = new TextDecoder().decode(bytes);
			return;
		}

		const text = new TextDecoder().decode(bytes);

		console.log(text);
	});

	response.once('error', console.error);
});

request.once('error', console.error);
request.end();
```

With Got, this becomes:

```js
import got from 'got';

try {
	const {body} = await got('https://httpbin.org/anything');
	console.log(body);
} catch (error) {
	console.error(error);
}
```

Much cleaner. But what about streams?

```js
import http from 'node:http';
import fs from 'node:fs';

const source = fs.createReadStream('article.txt');

const request = http.request('https://httpbin.org/anything', {
	method: 'POST'
}, response => {
	response.pipe(fs.createWriteStream('httpbin.txt'));
});

source.pipe(request);
```

Well, it's easy as that:

```js
import got from 'got';
import {pipeline as streamPipeline} from 'node:stream/promises';
import fs from 'node:fs';

await streamPipeline(
	fs.createReadStream('article.txt'),
	got.stream.post('https://httpbin.org/anything'),
	fs.createWriteStream('httpbin.txt')
);
```

The advantage is that Got also handles errors automatically, so you don't have to create custom listeners.

Furthermore, Got supports redirects, compression, advanced timeouts, cache, pagination, cookies, hooks, and more!

#### What next?

Unfortunately Got options differ too much from the Node.js options. It's not possible to provide a brief summary.\
Don't worry, you will learn them fast - they are easy to understand! Every option has an example attached.

Take a look at the [documentation](../../readme.md#documentation). It's worth the time to read it.\
There are [some great tips](../tips.md).

If something is unclear or doesn't work as it should, don't hesitate to [open an issue](https://github.com/sindresorhus/got/issues/new/choose).

---

### Documentation Migration Guides Request (`documentation/migration-guides/request.md`)

[> Back to homepage](../../readme.md#documentation)

## Migration guides

> You may think it's too hard to switch, but it's really not. 🦄

### Request

Let's take the very first example from [Request's readme](https://github.com/request/request#super-simple-to-use):

```js
import request from 'request';

request('https://google.com', (error, response, body) => {
	console.log('error:', error);
	console.log('statusCode:', response && response.statusCode);
	console.log('body:', body);
});
```

With Got, it is:

```js
import got from 'got';

try {
	const response = await got('https://google.com');
	console.log('statusCode:', response.statusCode);
	console.log('body:', response.body);
} catch (error) {
	console.log('error:', error);
}
```

Looks better now, huh? 😎

#### Common options

These Got options are the same as with Request:

- [`url`](../2-options.md#url)
- [`body`](../2-options.md#body)
- [`followRedirect`](../2-options.md#followredirect)
- [`encoding`](../2-options.md#encoding)
- [`maxRedirects`](../2-options.md#maxredirects)
- [`localAddress`](../2-options.md#localaddress)
- [`headers`](../2-options.md#headers)
- [`createConnection`](../2-options.md#createconnection)
- [UNIX sockets](../2-options.md#enableunixsockets): `http://unix:SOCKET:PATH`

The `time` option does not exist, assume [it's always true](../6-timeout.md).

So if you're familiar with these, you're good to go.

#### Renamed options

**Note:**
> - Got stores HTTPS options inside [`https`](../2-options.md#https). Some of them have been renamed. [Read more](../5-https.md).

Readability is very important to us, so we have different names for these options:

- `qs` → [`searchParams`](../2-options.md#searchparams)
- `strictSSL` → [`rejectUnauthorized`](../2-options.md#rejectunauthorized)
- `gzip` → [`decompress`](../2-options.md#decompress)
- `jar` → [`cookieJar`](../2-options.md#cookiejar) (accepts [`tough-cookie`](https://github.com/salesforce/tough-cookie) jar)
- `jsonReviver` → [`parseJson`](../2-options.md#parsejson)
- `jsonReplacer` → [`stringifyJson`](../2-options.md#stringifyjson)

#### Changes in behavior

- The [`agent` option](../2-options.md#agent) is now an object with `http`, `https` and `http2` properties.
- The [`timeout` option](../6-timeout.md) is now an object. You can set timeouts on particular events!
- The [`searchParams` option](../2-options.md#searchparams) is always serialized using [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams).
- In order to pass a custom query string, provide it with the `url` option.\
  `got('https://example.com', {searchParams: {test: ''}})` → `https://example.com/?test=`\
  `got('https://example.com/?test')` → `https://example.com/?test`
- To use streams, call `got.stream(url, options)`.

#### Breaking changes

- The `json` option is not a `boolean`, it's an `object`. It will be stringified and used as a body.
- The `form` option is an `object` and will be used as `application/x-www-form-urlencoded` body.
- All headers are converted to lowercase.\
  According to [the spec](https://datatracker.ietf.org/doc/html/rfc7230#section-3.2), the headers are case-insensitive.
- No `oauth` / `hawk` / `aws` / `httpSignature` option.\
  To sign requests, you need to create a [custom instance](../examples/advanced-creation.js).
- No `agentClass` / `agentOptions` / `pool` option.
- No `forever` option.\
  You need to pass an agent with `keepAlive` option set to `true`.
- No `proxy` option. You need to [pass a custom agent](../tips.md#proxying).
- No `auth` option.\
  You need to use [`username`](../2-options.md#username) / [`password`](../2-options.md#password) instead or set the `authorization` header manually.
- No `baseUrl` option.\
  Instead, there is [`prefixUrl`](../2-options.md#prefixurl) which appends a trailing slash if not present.
- No `removeRefererHeader` option.\
  You can remove the `referer` header in a [`beforeRequest` hook](../9-hooks.md#beforerequest).
- No `followAllRedirects` option.
- [`copyPipedHeaders`](../2-options.md#copypipedheaders) defaults to `false`.\
  Piped request headers are no longer copied automatically. Opt in with `copyPipedHeaders: true` for proxy scenarios.
- With `copyPipedHeaders: true`, explicitly set headers win over piped headers.\
  Piped headers only fill headers that were not explicitly set.
- When copying piped headers, Got omits credentials, hop-by-hop headers, and headers nominated by `Connection`/`Proxy-Connection`.\
  See [`copyPipedHeaders`](../2-options.md#copypipedheaders) for the full list.

Hooks are very powerful. [Read more](../9-hooks.md) to see what else you achieve using hooks.

#### More about streams

Let's take a quick look at another example from Request's readme:

```js
http.createServer((serverRequest, serverResponse) => {
	if (serverRequest.url === '/doodle.png') {
		serverRequest.pipe(request('https://example.com/doodle.png')).pipe(serverResponse);
	}
});
```

Request can proxy headers with the stream. Got can do that too, but it is opt-in:

```js
import {pipeline as streamPipeline} from 'node:stream/promises';
import got from 'got';

const server = http.createServer(async (serverRequest, serverResponse) => {
	if (serverRequest.url === '/doodle.png') {
		await streamPipeline(
			serverRequest,
			got.stream('https://example.com/doodle.png', {copyPipedHeaders: true}),
			serverResponse
		);
	}
});

server.listen(8080);
```

In terms of stream usage, nothing has really changed, but header proxying is opt-in via `copyPipedHeaders: true`.

#### Convenience methods

- If you were using `request.get`, `request.post`, and so on - you can do the same with Got.
- The `request.defaults({…})` method has been renamed. You can do the same with `got.extend({…})`.
- There is no `request.cookie()` nor `request.jar()`. You have to use `tough-cookie` directly.

#### You're good to go!

Well, you have already come this far :tada:\
Take a look at the [documentation](../../readme.md#documentation). It's worth the time to read it.\
There are [some great tips](../tips.md).

If something is unclear or doesn't work as it should, don't hesitate to [open an issue](https://github.com/sindresorhus/got/issues/new/choose).

---

--- METRICS ---
- Files Extracted: 21
- Estimated Token Budget: ~44124 tokens
- Canonical Reference: https://codewiki.google/github.com/sindresorhus/got
