> ## Documentation Index
> Fetch the complete documentation index at: https://docs.unprice.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# TypeScript SDK

> The @unprice/budgets client: operations, handles, errors, and options.

`@unprice/budgets` is the budget client on its own. `@unprice/api` includes it and re-exports its
handles, errors, and types, so code moves between the two packages by changing the import, and
`instanceof` checks match errors from either.

<CodeGroup>
  ```bash npm theme={null}
  npm install @unprice/budgets
  ```

  ```bash pnpm theme={null}
  pnpm add @unprice/budgets
  ```

  ```bash yarn theme={null}
  yarn add @unprice/budgets
  ```

  ```bash bun theme={null}
  bun add @unprice/budgets
  ```
</CodeGroup>

```ts theme={null}
import { Unprice } from "@unprice/budgets";

const unprice = new Unprice({ token: process.env.UNPRICE_TOKEN! });
```

## Operations

`unprice.budgets` calls the API directly. Each method returns `{ result }` or `{ error }`. Check
`error` before you use `result`.

| Method | Endpoint |
| - | - |
| `budgets.upsert(input)` | `POST /v1/budgets/upsert` |
| `budgets.get({ key })` | `GET /v1/budgets/get` |
| `budgets.authorize(input)` | `POST /v1/budgets/authorize` |
| `budgets.settle(input)` | `POST /v1/budgets/settle` |
| `budgets.release(input)` | `POST /v1/budgets/release` |
| `budgets.charge(input)` | `POST /v1/budgets/charge` |
| `budgets.record(input)` | `POST /v1/budgets/record` |
| `budgets.lease(input)` | `POST /v1/budgets/lease` |
| `budgets.report(input)` | `POST /v1/budgets/report` |

The request and response schemas are in the [API reference](/api-reference/overview).

## Handles

`unprice.budget(key)` returns a handle bound to one key. It never creates or changes the budget.

| Member | Returns |
| - | - |
| `key` | The budget key |
| `spend(input, fn)` | The `value` your function returns. Throws the [budget errors](/budgets/errors). |
| `authorize({ amount, idempotencyKey, ttlMs? })` | `{ result: { allowed: true, hold, balance, replayed } }` or `{ result: { allowed: false, blocked, balance } }` |
| `charge({ amount, idempotencyKey })` | `{ result: { allowed: true, amount, crossed, replayed, balance } }` or `{ result: { allowed: false, blocked, balance } }` |
| `record({ amount, idempotencyKey, occurredAt })` | `{ result: { amount, crossed, replayed, balance } }` |
| `get()` | `{ result: { budget, history } }` |

Each method except `spend` can also return `{ error }`. A hold from `authorize` has `id`, `key`,
`amount`, `expiresAt`, `settle({ amount })`, and `release()`. See
[spend against a budget](/budgets/spend).

### `spend` input

<ParamField body="estimate" type="string | number" required>
  The most the work may use, in the budget's unit. `spend` authorizes this amount before `fn` runs.
</ParamField>

<ParamField body="idempotencyKey" type="string">
  A stable key for this operation, such as the request id. When you omit it, `spend` generates one
  and puts it on its errors as `error.idempotencyKey`.
</ParamField>

<ParamField body="ttlMs" type="integer">
  How long the hold stays open. Defaults to the budget's `holds.defaultTtlMs`.
</ParamField>

`fn` receives the hold and returns `{ value, actual }`. `spend` settles `actual`, which may not
exceed `estimate`, and returns `value`.

### Local handles

`unprice.budget(key, { mode: "local", lease })` returns a [local handle](/budgets/local-mode). It
has the same members, plus `mode: "local"`, `flush()`, and `close()`. `unprice.close()` returns
every open lease. It resolves with a `{ key, error }` for each lease it could not return.

Amounts accept numbers or decimal strings and come back as decimal strings. Numbers are rounded to
8 decimal places.

## Errors

| Class | Thrown when |
| - | - |
| `BudgetExceededError` | A limit denied the estimate |
| `BudgetUnavailableError` | Unprice could not be reached, or answered 429 or 500, after the client's retries |
| `BudgetRequestError` | The API rejected the authorization |
| `BudgetRecoveryError` | Your function threw, or your idempotency key replayed |
| `BudgetSettleError` | The work succeeded, but its settle failed |

[Errors and recovery](/budgets/errors) explains each one.

## Client options

<ParamField body="token" type="string" required>
  A project API key. Use a runtime key where the code spends, and a config key only where caps are
  raised.
</ParamField>

<ParamField body="baseUrl" type="string" default="https://api.unprice.dev">
  Send requests to your own Unprice deployment.
</ParamField>

<ParamField body="retry" type="{ attempts?: number; backoff?: (retryCount: number) => number }">
  Retries on network and server errors. `attempts` defaults to 2, so a call makes at most three
  requests.
</ParamField>

<ParamField body="waitUntil" type="(promise: Promise<unknown>) => void">
  Keeps a serverless runtime alive after the response. With it, `spend` returns once your function
  finishes and settles in the background.
</ParamField>

<ParamField body="onSettleError" type="(error: BudgetSettleError) => void">
  Receives a background settle that failed. Its hold stays open and counts its full estimate when
  it expires.
</ParamField>

<ParamField body="fetch" type="(input: Request) => Promise<Response>">
  A custom `fetch` for non-standard runtimes or tests.
</ParamField>

<ParamField body="headers" type="Record<string, string>">
  Additional headers to send with each request.
</ParamField>

<ParamField body="cache" type="RequestCache" default="no-store">
  The `fetch` cache mode.
</ParamField>

<ParamField body="debug" type="boolean" default="false">
  Log retry attempts with `console.debug`.
</ParamField>

<ParamField body="disableTelemetry" type="boolean" default="false">
  Stop sending anonymous telemetry: runtime, platform, and SDK version.
</ParamField>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.