---
title: exponentialBackoff
description: Retry with exponentially growing delays, capped at an upper bound.
sidebar:
  label: ExponentialBackoff
type: package
package: "@zap-studio/retry"
---

`exponentialBackoff(...)` creates a policy that doubles the delay after each failure, up to a hard cap, reducing pressure on unstable upstream services.

## Import

```ts
import { exponentialBackoff } from "@zap-studio/retry";
```

## Configuration

```ts
import { exponentialBackoff } from "@zap-studio/retry";

const policy = exponentialBackoff({
  maxAttempts: 6,
  baseDelayMs: 100,
  maxDelayMs: 5_000,
});
```

| Option        | Type                                 | Default | Description                                                        |
| ------------- | ------------------------------------ | ------- | ------------------------------------------------------------------ |
| `maxAttempts` | `number`                             | —       | Maximum number of attempts (including the first) before giving up. |
| `baseDelayMs` | `number`                             | —       | Initial delay in milliseconds, doubled after each retry.           |
| `maxDelayMs`  | `number`                             | —       | Hard upper bound in milliseconds for the computed delay.           |
| `jitter`      | `"full" \| "equal" \| JitterOptions` | —       | Randomizes the capped delay. See [Jitter](/retry/jitter).          |

All options are required except `jitter`.

## Delay Model

The delay before the next retry is computed from the failed attempt number:

```ts
delayMs = Math.min(maxDelayMs, baseDelayMs * 2 ** (attempt - 1));
```

With `baseDelayMs: 100` and `maxDelayMs: 2_000`:

- after attempt 1 → wait `100` ms
- after attempt 2 → wait `200` ms
- after attempt 3 → wait `400` ms
- after attempt 4 → wait `800` ms
- after attempt 5 → wait `1_600` ms
- after attempt 6 → wait `2_000` ms (capped)

The policy stops retrying when `attempt >= maxAttempts`, returning a decision with `reason: "max-attempts-reached"`.

:::note

Delays are deterministic by default. Pass `jitter: "full"` or `jitter: "equal"` to randomize them — see [Jitter](/retry/jitter).

:::

## When to Use

Use `exponentialBackoff` when:

- retries target shared or networked services
- transient failures are common
- aggressive immediate retries could amplify outages

For predictable, constant intervals, use [`fixedDelay`](/retry/fixed-delay) instead.

## Example

```ts
import { exponentialBackoff, runRetryPolicy } from "@zap-studio/retry";
import { $fetch } from "@zap-studio/fetch";

const policy = exponentialBackoff({
  maxAttempts: 5,
  baseDelayMs: 100,
  maxDelayMs: 2_000,
});

const user = await runRetryPolicy(policy, async () => {
  const response = await $fetch("https://api.example.com/users/1", {
    throwOnFetchError: true,
  });
  return await response.json();
});
```
