got Documentation

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

14,952 stars TypeScript TypeScript 21 files · ~44,204 tokens #http#http-client#http-request#https#nodejs#npm-package
RAW Doc

Technical Documentation: sindresorhus/got

ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference

Overview (`readme.md`)

text
**Fast remote container builds and GitHub Actions runners.**
	</a>
	
	
	

<hr>

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


See how Got compares to other HTTP libraries


You probably want Ky instead, by the same people. It's smaller, works in the browser too, and is more stable since it's built on Fetch. Or fetch-extras for simple needs.


Support questions should be asked here.

Install

sh
npm install got

Warning: This package is native ESM and no longer provides a CommonJS export. If your project uses CommonJS, you will have to convert to ESM. 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 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 and stringifyJson options.

For more useful tips like this, visit the Tips page.

Highlights

Documentation

By default, Got will retry on failure. To disable this option, set options.retry.limit to 0.

Main API

Timeouts and retries

Advanced creation

Cache, Proxy and UNIX sockets

Integration


Migration guides

Got plugins

  • got4aws - Got convenience wrapper to interact with AWS v4 signed APIs
  • gh-got - Got convenience wrapper to interact with the GitHub API
  • gl-got - Got convenience wrapper to interact with the GitLab API
  • gotql - Got convenience wrapper to interact with GraphQL using JSON-parsed queries instead of strings
  • got-fetch - Got with a fetch interface
  • got-scraping - Got wrapper specifically designed for web scraping purposes
  • got-ssrf - Got wrapper to protect server-side requests against SSRF attacks

Comparison

got node-fetch ky axios superagent
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
Issues closed
Downloads
Coverage TBD
Build
Bugs
Dependents
Install size
GitHub stars
TypeScript support
Last commit

* 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.

Click here to see the install size of the Got dependencies.

Maintainers

Sindre Sorhus Szymon Marczak

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

> Back to homepage

Promise API

Source code: source/as-promise/index.ts

The main Got function returns a Promise.
Request aborting is supported via the signal option and AbortController.

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

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

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

Returns: Promise<T>

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

It is semantically the same as setting options.resolveBodyOnly to true and options.responseType to 'json'.

`promise.buffer()`

Returns: Promise

A shortcut method that gives a Promise returning a Uint8Array.

It is semantically the same as setting options.resolveBodyOnly to true and options.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 to true and options.responseType to 'text'.

`promise.on(event, handler)`

The events are the same as in Stream API.

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

`promise.off(event, handler)`

Removes listener registered with promise.on.

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

Instances

Source code: source/create.ts

`got.defaults`

`options`

Type: Options

The options used for this instance.

`handlers`

Type: Handler[]

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

An array of handlers. The next function returns a Promise or a Request Got stream.

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

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

Options

Source code: 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 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: string | [URL](https://nodejs.org/api/url.html#url_the_whatwg_url_api)

The URL to request. Usually the url represents a WHATWG URL.
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.
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: 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;

WHATWG URL Search Params 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, 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 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

You can abort the request using 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 used to make the request.
Common methods include: GET, HEAD, POST, PUT, DELETE.
Got also supports QUERY, which is defined in RFC 10008.
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 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().

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:

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 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 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' 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: object&lt;string, [Primitive](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html)&gt;

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.

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 doesn't specify any particular behavior for the GET method having a payload, therefore it's considered an 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.

**Merge behavior:**

  • Overrides existing properties.

`retry`

Type: object

See the Retry API.

**Merge behavior:**

  • Overrides existing properties.

`hooks`

Type: object

See the Hooks API.

**Merge behavior:**

  • Merges arrays via [...hooksArray, ...next]

`encoding`

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

Encoding to be used on setEncoding of the response data.

To get a 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 instead of the Response object.

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: object | [tough.cookieJar](https://github.com/salesforce/tough-cookie#cookiejar)

**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 for more information.

`cookieJar.getCookieString`

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

See ToughCookie API 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. You can optionally turn on this behavior also for other redirect codes - see 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 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 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 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

Custom DNS resolution logic.

The function signature is the same as dns.lookup.

`dnsCache`

Type: {lookup: Function; clear?: Function} | boolean

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: 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>
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 for storing cached response data.

`cacheOptions`

Type: object
Default: {}

Cache 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 support. However, you can provide a h2session option in a beforeRequest hook. See an example.
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 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.

`password`

Type: string
Default: ''

The password used for Basic 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.

`pagination`

Type: object

See Pagination API.

`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 (default 16KB: 16384).

`methodRewriting`

Type: boolean
Default: false

Specifies if the HTTP request method should be rewritten as GET on redirects.

As the specification 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:

`enableUnixSockets`

Type: boolean
Default: false

When enabled, requests can also be sent via UNIX Domain 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, 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.

`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 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

Stream API

Source code: 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

This constructor takes the same arguments as the Got promise.

Note:

When piping to 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 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

The options used to make the request.

`stream.response`

Type: IncomingMessage

The underlying IncomingMessage instance.

`stream.requestUrl`

Type: URL

The current URL object in this try.

`stream.redirectUrls`

Type: URL[]

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

An object representing how much data have been downloaded.

`stream.uploadProgress`

Type: 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 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

An object representing performance information.

Got computes the timings itself, with the same phases 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

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

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

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

`progress`

Type: Progress

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

`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:

`retryCount`

Type: number

The current retry count.

`error`

Type: 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

The new options used to make the next request.

`response`

Type: 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

Extends: 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, 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.

`statusCode`

Type: number

The HTTP status code.

`url`

Type: string

The final URL after all redirects.

`timings`

Type: 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.

`statusMessage`

Type: string

The status message corresponding to the status code.


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

> Back to homepage

Pagination API

Source code: source/create.ts

The RFC5988 defines how the Link header looks like.

When the response has been processed, Got looks for the reference of the next relation.
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, you need to customize the paginate function below.

`got.paginate(url, options?)`

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

Returns an async iterator.

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);

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

Parses a Link 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(…) 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 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.

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 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:

`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.

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

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

> Back to homepage

Advanced HTTPS API

`https`

Type: object

This option represents the options used to make HTTPS requests.

`alpnProtocols`

Type: string[]
Default: undefined

Acceptable ALPN 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 for an example.

`serverName`

Type: string
Default: undefined

Server name for the Server Name Indication (SNI) 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 for better readability.

Overrides trusted CA certificates.

Defaults to CAs provided by Mozilla.

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.

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 for better readability.

Certificate chains in PEM format.

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

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

Examples

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

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

> Back to homepage

Timeout options

Source code: 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 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) which calls socket.setTimeout(timeout) 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

Retry API

Note:

If you're looking for retry implementation using streams, check out the Retry Stream API.

Tip:

You can trigger a retry by throwing the RetryError in any hook.

Tip:

The afterResponse hook exposes a dedicated function to retry with merged options. Read more.

`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.

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:

The allowed HTTP status codes to retry on.

`errorCodes`

Type: string[]

The allowed error codes to retry on.

  • ETIMEDOUT - One of the timeout limits 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. 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

Errors

Source code:

All Got errors contain various metadata, such as:

  • code - A string like ERR_NON_2XX_3XX_RESPONSE,
  • options - An instance of Options,
  • 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.

[!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 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, 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. 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().


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

> Back to homepage

Hooks API

`hooks`

Type: object<string, Function[]>

This option represents the hooks to run. Thrown errors will be automatically converted to 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 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:

`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 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

Capturing async stack traces

Caution:

  • Capturing async stack traces can severely degrade performance!

Want to skip the article? See the 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 Promises. However, 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 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:

text
/* 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);
}
text
/* 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:


Documentation Cache (`documentation/cache.md`)

> Back to homepage

Cache

Got implements RFC 7234 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.

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 internally to support a wide range of storage adapters. For something more scalable you could use an official Keyv storage adapter:

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 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

Diagnostics Channel

Got integrates with Node.js diagnostic channels 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

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.
It looks pretty complicated, but... it's simple and extremely useful.

Before we start, we need to find the GitHub API docs.

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.

  1. 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:

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. 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 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 Promise<[Response](3-streams.md#response-2)>. 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 [got.text](1-promise.md#promisetext) 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 allows to leverage Node.js 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: A query string object.
  • prefixUrl: Prepended to query paths. Paths must be relative to prefix, i.e. not begin with a /.
  • method: The HTTP method name.
  • headers: Query headers.
  • json: JSON body.
  • form: A form query string object.

See the documentation for other 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.

Retries

A failed request is retried twice. The retry policy may be tuned with a 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', …).

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.

Going further

There is a lot more to discover in the documentation and tips. Among others, Got can handle cookies, pagination, cache. Please read the documentation before implementing something that is already done by Got :innocent:.


Documentation Tips (`documentation/tips.md`)

> Back to homepage

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.

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.

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 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 package.

This is an example for querying an API Gateway 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 to retrieve the next page.
However, this behavior can be customized, see the Pagination API.

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 or response.request.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]

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))
});

UNIX Domain Sockets

See the enableUnixSockets option.

Testing

Got uses the native http module, which depends on the native net module.
This means there are two possible ways to test:

  1. Use a mocking library like 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 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 package is unmaintained. Use at your own risk.
  • The proxy-agent family doesn't follow newest Node.js features and lacks support.

Although there isn't a perfect, bug-free package, Apify's solution is a modern one.
See got-scraping/src/agent/h1-proxy-agent.ts. It has the same API as hpagent.

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 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 support.

However, you can provide a h2session option in a beforeRequest hook. See an example.

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.

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 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 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

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`](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`](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`](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`](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`](https://github.com/sindresorhus/got/blob/215e06a4993329578d92d4f44607913239a03094/source/core/options.ts#L382)

[`PaginationOptions`](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`](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

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:

Renamed options

We deeply care about readability, so we renamed these options:

Changes in behavior

  • transformRequest → hooks.beforeRequest
    • The API is different.
  • transformResponse → hooks.afterResponse
    • The API is different.
  • baseUrl → prefixUrl
    • The prefixUrl is prepended to relative string input. Absolute URLs bypass prefixUrl by default. Use allowAbsoluteUrls: false to reject absolute URLs when prefixUrl is set.
  • timeout
    • This option is now an object. You can now set timeouts on particular events!
  • responseType
    • Accepts 'text', 'json' or 'buffer'.

Breaking changes

Response

The response object is different as well:

The response.headers object remains the same.

Interceptors

Got offers hooks instead, which are more flexible.

Errors

Errors look the same, with the difference error.request returns a Got stream. Furthermore, Got provides more details to make debugging easier.

Abort

Got supports AbortController via the 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. It's worth the time to read it.
There are some great tips.

If something is unclear or doesn't work as it should, don't hesitate to open an issue.


Documentation Migration Guides Nodejs (`documentation/migration-guides/nodejs.md`)

> Back to homepage

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. It's worth the time to read it.
There are some great tips.

If something is unclear or doesn't work as it should, don't hesitate to open an issue.


Documentation Migration Guides Request (`documentation/migration-guides/request.md`)

> Back to homepage

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:

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:

The time option does not exist, assume it's always true.

So if you're familiar with these, you're good to go.

Renamed options

Note:

  • Got stores HTTPS options inside https. Some of them have been renamed. Read more.

Readability is very important to us, so we have different names for these options:

Changes in behavior

  • The agent option is now an object with http, https and http2 properties.
  • The timeout option is now an object. You can set timeouts on particular events!
  • The searchParams option is always serialized using 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, the headers are case-insensitive.
  • No oauth / hawk / aws / httpSignature option.
    To sign requests, you need to create a custom instance.
  • 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.
  • No auth option.
    You need to use username / password instead or set the authorization header manually.
  • No baseUrl option.
    Instead, there is prefixUrl which appends a trailing slash if not present.
  • No removeRefererHeader option.
    You can remove the referer header in a beforeRequest hook.
  • No followAllRedirects option.
  • 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 for the full list.

Hooks are very powerful. Read more 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. It's worth the time to read it.
There are some great tips.

If something is unclear or doesn't work as it should, don't hesitate to open an issue.


--- METRICS ---