# Retry failed requests

Configure retry limits, eligible methods and statuses, and the delay between attempts; add a custom decision when the default retry rules do not match your API. Use retries for requests that can safely be repeated after a transient failure.

## Set retry limits and eligibility

Set retry defaults on a [`ky` instance](https://bench-ky-6l.atloria.app/p/bench-ky-6l-QSDF7DxJ6h/developer/ky#default) when several calls share the same policy, or pass `retry` on an individual request. The value can be a number, which sets only the retry limit, or a [`RetryOptions`](https://bench-ky-6l.atloria.app/p/bench-ky-6l-QSDF7DxJ6h/developer/ky-retryoptions#retryoptions) object.

1. Create an instance with the retry policy and make a request:

   ```ts
   import ky from 'ky';

   const api = ky.extend({
   	retry: {
   		limit: 3,
   		methods: ['get'],
   		statusCodes: [429, 503],
   		afterStatusCodes: [429, 503],
   		maxRetryAfter: 30_000,
   		backoffLimit: 2_000,
   		jitter: true,
   	},
   });

   const run = async (): Promise<void> => {
   	const report = await api.get('https://api.example.com/reports').json<unknown>();
   	console.log(report);
   };

   void run();
   ```

   The instance makes GET requests eligible for up to three retries on the selected statuses. For those statuses, Ky uses retry timing headers when available and caps a header-provided delay at 30 seconds. Otherwise it uses the configured backoff and jitter. If an attempt succeeds, `.json()` resolves with the parsed response body; if retries are exhausted, the request rejects with its error.

## Customize the retry decision

Use `shouldRetry` to decide whether a failed attempt merits another try. Ky calls it only after the retry limit and method checks pass. Return `true` to retry, `false` to stop, or `undefined` (or no value) to keep Ky's default decision. A `true` result bypasses the default timeout and status-code checks, but not the retry limit or method checks.

1. Add a decision for rate limits while leaving other failures to the default rules:

   ```ts
   import ky, {HTTPError} from 'ky';

   const api = ky.extend({
   	retry: {
   		limit: 3,
   		methods: ['get'],
   		statusCodes: [429, 503],
   		shouldRetry: ({error, retryCount}) => {
   			if (error instanceof HTTPError) {
   				const status = error.response.status;

   				if (status === 429) {
   					return retryCount <= 2;
   				}

   				if (status >= 400 && status < 500) {
   					return false;
   				}
   			}

   			return undefined;
   		},
   	},
   });

   const run = async (): Promise<void> => {
   	const report = await api.get('https://api.example.com/reports').json<unknown>();
   	console.log(report);
   };

   void run();
   ```

   `retryCount` starts at `1` for the first retry. This policy allows at most two retries for `429`, rejects other `4xx` failures without retrying, and delegates other failures to Ky's default checks. For example, a `503` remains eligible under the configured `statusCodes`; recognized network errors also use the default behavior for retriable methods. `shouldRetry` decides whether Ky retries. The `beforeRetry` hook runs only after Ky confirms the retry and is where you modify the request.

## Retry options

The defaults below apply when you provide an object and omit a field. Set `limit` to `0` to disable retries. A numeric `retry` value is shorthand for `limit`; the other defaults remain in effect.

| Option | Type | Default | What it does |
| --- | --- | --- | --- |
| `limit` | `number` | `2` | Sets the number of retries. It must be a finite, non-negative integer. |
| `methods` | `readonly HttpMethod[]` | `['get', 'put', 'head', 'delete', 'options', 'trace', 'query']` | Restricts retries to these HTTP methods. |
| `statusCodes` | `readonly number[]` | `[408, 413, 429, 500, 502, 503, 504]` | Lists HTTP response statuses eligible for retry. `413` is retried only when the response has a retry timing header. |
| `afterStatusCodes` | `readonly number[]` | `[413, 429, 503]` | Selects statuses that use retry timing headers. Each status must also appear in `statusCodes`. |
| `maxRetryAfter` | `number` | `Infinity` | Caps a delay obtained from a retry timing header. |
| `backoffLimit` | `number` | `Infinity` | Caps each calculated retry delay in milliseconds. |
| `delay` | `(attemptCount: number) => number` | `attemptCount => 0.3 * (2 ** (attemptCount - 1)) * 1000` | Calculates a delay in milliseconds; the attempt count starts at `1`. |
| `jitter` | `boolean \| ((delay: number) => number)` | `undefined` | Adds random variation to calculated retry delays. `true` uses full jitter. Jitter does not change a delay supplied by a server timing header. |
| `retryOnTimeout` | `boolean` | `false` | Enables retries when a request times out before a response arrives. A timeout while a shortcut method reads a received response body is not retried. |
| `shouldRetry` | `(state: ShouldRetryState) => boolean \| void \| Promise<boolean \| void>` | `undefined` | Applies a custom retry decision after limit and method checks pass. |

## Pitfalls

- With retries enabled, Ky buffers a streaming request body in memory so it can replay it. For a large streaming upload that does not need retries, set `retry: {limit: 0}`.
- A retry request returned from `beforeRetry` or supplied to `ky.retry({request})` is used as-is and is not sanitized. If it targets another origin, remove credentials you do not want forwarded.
- Ky automatically retries recognized network errors for retriable methods. Other unrecognized errors are not retried by default; use `shouldRetry` when your runtime or application needs a different decision.
- When Ky populates `HTTPError.data`, it consumes the response body. Do not then call `error.response.json()` or another body method; use `error.data` instead. See [Ky errors and validation](https://bench-ky-6l.atloria.app/p/bench-ky-6l-QSDF7DxJ6h/developer/ky-errors-and-validation).

## Related

- [How Ky works](https://bench-ky-6l.atloria.app/p/bench-ky-6l-QSDF7DxJ6h/developer/how-ky-works)
- [The request lifecycle](https://bench-ky-6l.atloria.app/p/bench-ky-6l-QSDF7DxJ6h/developer/the-request-lifecycle)
- [Ky errors and validation](https://bench-ky-6l.atloria.app/p/bench-ky-6l-QSDF7DxJ6h/developer/ky-errors-and-validation)
