---
title: Getting Started
description: Install @zap-studio/retry and build your first retry policy step by step.
type: package
package: "@zap-studio/retry"
---

## Installation

<CodeGroup>

```bash npm
npm install @zap-studio/retry
```

```bash yarn
yarn add @zap-studio/retry
```

```bash pnpm
pnpm add @zap-studio/retry
```

```bash bun
bun add @zap-studio/retry
```

```bash deno
deno add jsr:@zap-studio/retry
```

</CodeGroup>

## Create a Policy

Import a built-in policy factory and configure it. `exponentialBackoff(...)` doubles the delay after each failure, up to a cap.

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

const policy = exponentialBackoff({
  maxAttempts: 5, // total attempts, including the first
  baseDelayMs: 100, // first retry waits 100 ms
  maxDelayMs: 2_000, // later retries never wait longer than 2 s
});
```

## Run Your Work

Pass the policy and an async function to `runRetryPolicy(...)`. The runner calls the function once per attempt, catches failures, waits, and retries until it succeeds or the policy stops.

```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 users = await runRetryPolicy(policy, async () => {
  const response = await $fetch("https://api.example.com/users", {
    throwOnFetchError: true,
  });
  return await response.json();
});
```

:::note

The example uses [`fetch`](/fetch), but any function that returns a promise works — the runner retries whenever the function throws or rejects.

:::

## Handle Exhaustion

By default, `runRetryPolicy(...)` throws a `RetryError` when every attempt fails.

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

try {
  const users = await runRetryPolicy(policy, fetchUsers);
  console.log(users);
} catch (error) {
  if (error instanceof RetryError) {
    console.error("Retries exhausted after", error.attempts, "attempts");
    console.error("Last error:", error.lastError);
  } else {
    throw error;
  }
}
```

Prefer result objects over exceptions? Pass `throwOnExhausted: false` to receive a discriminated union instead:

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

const result = await runRetryPolicy(policy, fetchUsers, {
  throwOnExhausted: false,
});

if (result.ok) {
  console.log(result.value);
} else {
  console.error("Failed after", result.attempts, "attempts");
  if (result.error instanceof RetryError) {
    console.error("Last error:", result.error.lastError);
  }
}
```
