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

# Credits and usage

> What a call costs, and how to spend less.

Unif bills in credits, and the unit is a **verified hit** — one row that a source actually
returned and that passed verification.

That follows directly from [the waterfall](/docs/concepts/waterfall). A request runs down the
cascade until something verifies; the steps that missed cost nothing, and the steps never reached
cost nothing. You are charged once, for the answer.

Two consequences worth internalizing:

* **A request is not a unit.** Paging with `limit=100` costs exactly what paging with `limit=25`
  does, because the cost is in the rows, not the calls.
* **A miss is free.** A search matching nothing, an enrichment that resolves `not_found`, a
  cascade that exhausts without verifying — all zero.

<Note>
  This page covers the consumption model — how many credits a call consumes. Plan sizes and
  pricing live in your dashboard at [app.unif.dev](https://app.unif.dev).
</Note>

## What costs what

| Call                                          | Cost                                      |
| --------------------------------------------- | ----------------------------------------- |
| `GET /me`, `/channels`, `/coverage`, `/usage` | Free                                      |
| List, tracker and webhook management          | Free                                      |
| `POST /*/search`                              | 1 credit per verified row returned        |
| `GET /{entity}/{id}`                          | 1 credit                                  |
| `GET /{entity}/{id}/timeseries`               | 1 credit per bucket returned              |
| `GET /lists/{id}/items`                       | 1 credit per row returned                 |
| `POST /resolve`                               | 1 credit per input resolved               |
| `POST /enrich`                                | 2 credits per row, or 1 with `fields` set |
| Job — `enrich` or `search`                    | Same per-row rate as the synchronous call |
| Job — `export`                                | 1 credit per row exported                 |
| Tracker cycle                                 | 1 credit per entity checked               |

Three rules follow from this, and they cover most of what you need to know:

<CardGroup cols={3}>
  <Card title="You pay for hits" icon="circle-check">
    Cascade steps that miss, fail verification, or are never reached are all free. Exploratory
    queries are cheap to get wrong.
  </Card>

  <Card title="Rows, not requests" icon="table-rows">
    Page size does not change cost. Always use `limit=100`.
  </Card>

  <Card title="Narrow fields, lower rate" icon="filter">
    Setting `fields` on an enrich call halves the per-row cost.
  </Card>
</CardGroup>

<Note>
  A deep cascade is not more expensive than a shallow one. If a row is answered at step 05 rather
  than step 01 you pay the same single credit — the cost of breadth is carried by Unif, not
  passed through per attempt.
</Note>

## Reading the cost of a call

Every billable response carries the cost in its headers:

```
X-Unif-Credits-Charged: 25
X-Unif-Credits-Remaining: 48210
```

Batch endpoints also report it in the body, which is easier to log:

```json theme={null}
{
  "object": "enrich_response",
  "credits_charged": 47,
  "data": [ /* … */ ]
}
```

<Tip>
  Log `X-Unif-Credits-Charged` next to `X-Request-Id` on every call. When usage jumps, the answer
  is already in your logs instead of requiring a reconstruction.
</Tip>

## The ledger

`GET /v1/usage` is the balance; `GET /v1/usage/events` is the itemized ledger.

```bash theme={null}
curl "https://api.unif.dev/v1/usage/events?start=2026-09-01T00:00:00Z" \
  -H "Authorization: Bearer $UNIF_API_KEY"
```

```json theme={null}
{
  "object": "list",
  "data": [
    {
      "id": "ue_01k3m9x7v2q8r4t6y0b1n5d7fa",
      "object": "usage_event",
      "endpoint": "POST /products/search",
      "rows": 100,
      "credits": 100,
      "request_id": "req_01k3m9x7v2q8r4t6y0b1n5d7fa",
      "created_at": "2026-09-15T02:14:08Z"
    }
  ]
}
```

Every event carries its `request_id`, so a surprising line in the ledger traces back to the exact
call that produced it — and to your own log line for it.

```python theme={null}
from collections import Counter

by_endpoint = Counter()
for event in paginate(session, ".../usage/events", params={"start": month_start}):
    by_endpoint[event["endpoint"]] += event["credits"]

for endpoint, credits in by_endpoint.most_common(10):
    print(f"{credits:>10,}  {endpoint}")
```

<Tip>
  Issue a separate API key per service. The ledger then attributes spend by service without any
  work on your side — see [Authentication](/docs/authentication#scope).
</Tip>

## Spending less

<AccordionGroup>
  <Accordion title="Filter before you page" icon="filter">
    The largest avoidable cost is paging through a broad result set and discarding most of it
    client-side. A revenue floor or a tighter category turns 1,000 charged rows into 80.
  </Accordion>

  <Accordion title="Set fields on enrichment" icon="list-check">
    `fields` halves the per-row rate and makes `meta.completeness` meaningful. There is no reason
    to request a full record you do not read.
  </Accordion>

  <Accordion title="Store IDs, do not re-resolve" icon="fingerprint">
    Resolving the same URL every night charges for identity you already established. Store the
    `shp_…` ID once — see [Identifiers](/docs/concepts/identifiers).
  </Accordion>

  <Accordion title="Prefer trackers to re-reading lists" icon="bell">
    A tracker charges per entity checked, the same as reading the list — but it only wakes your
    systems when something crossed a threshold, and it will not silently keep running against a
    list nobody looks at.
  </Accordion>

  <Accordion title="Delete trackers you stopped reading" icon="trash">
    A daily tracker over 500 entities is 15,000 credits a month whether or not anyone acts on it.
    This is the most common source of unexplained spend.
  </Accordion>

  <Accordion title="Develop against the sandbox" icon="flask">
    Sandbox keys return [fixture data](/docs/platform/sandbox) and cost nothing. Integration tests
    should never touch live credits.
  </Accordion>
</AccordionGroup>

## Running out

When a workspace exhausts its credits, billable calls return `402` with `insufficient_credits`.
Free endpoints keep working, so `GET /usage` still answers and health checks stay green.

```json theme={null}
{
  "error": {
    "type": "billing_error",
    "code": "insufficient_credits",
    "message": "This request costs 25 credits and 4 remain in the current period.",
    "request_id": "req_01k3m9x7v2q8r4t6y0b1n5d7fa"
  }
}
```

<Warning>
  `402` is not retryable. Retrying burns rate limit and changes nothing until credits are added or
  the period rolls over. Alert on it instead — and alert on `credits_remaining` falling below a
  threshold, well before it reaches zero.
</Warning>

Workspaces with overage enabled continue past the included allowance and are billed for the
difference. `overage_allowed` on `GET /usage` tells you which mode you are in.
