Credits and billing
Status:
betaCategory:referenceAdded:2026-06-22Last reviewed:2026-06-22
Summary
The API is metered in credits. Your account holds a prepaid credit
balance, and every successful call spends a fixed number of credits for
the route it hit. You see the cost of each call in the response headers,
and when your balance — or a budget you set — runs out, the next call is
refused with 402 PaymentRequired instead of doing any work.
Only requests that succeed are billed. A request that fails validation, authentication, or a rate-limit check costs nothing.
How billing works
- Credits are prepaid. Your balance is funded ahead of use and drawn down as you call the API. Manage the balance from the developer portal.
- Most routes have a fixed credit cost. A given endpoint costs the same whole number of credits on every successful call, independent of payload size. Heavier endpoints (composite analytics, AI-backed clinical surfaces) cost more credits than light lookups. A small number of routes are variable-cost — their charge scales with the size of the call (see Variable-cost endpoints).
- A unit price converts credits to dollars. One global dollars-per-credit rate turns the credit cost of a call into its dollar cost. Both the unit price and the per-call dollar cost are returned in the response headers.
- Only successful calls are billed. A response in the
2xx–3xxrange is billable; any4xxor5xxis not. This is the opposite of rate limits, where every request counts against your window regardless of outcome (see rate-limits.md).
What a call costs
The credit cost is fixed per route for most endpoints (and variable for a
few — see Variable-cost endpoints), and the exact
amount a call spent is returned to you on the response itself in
X-Credits-Used (credits) and X-Credits-Cost-USD (dollars). The developer
portal also shows your per-route usage and spend.
There is no static price list to memorize or hard-code — read the cost from the headers of a real call, or from the portal. Treat the headers as the source of truth for what you were charged.
Budgets
Beyond your funded balance, you can cap spend from the portal at two levels, the same way you cap rate limits:
| Level | Applies to |
|---|---|
| Account-wide cap | An optional ceiling across every key on your account. |
| Per-key allocation | A budget carved out for a single key. A key with no allocation draws from the unallocated balance. |
Budgets are entered in dollars in the portal and enforced in credits. A
call is checked against your balance and against every budget that
applies to it; the tightest one wins. When any of them is exhausted,
the call is refused with 402 (see below) — even if the others still
have room.
Credit headers
Every billable response carries the X-Credits-* set, the billing
sibling of the X-RateLimit-* headers:
| Header | Meaning |
|---|---|
X-Billable | true if this response was billed, false if not (any 4xx/5xx). |
X-Credits-Used | Credits this call spent. 0 when not billable. |
X-Credits-Cost-USD | Dollar cost of this call (credits × unit price). |
X-Credit-Unit-USD | Current dollars-per-credit unit price. |
X-Credits-Remaining | Projected balance after this call. Appears when your balance is known. |
A successful call looks like this (header values are illustrative):
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-Billable: true
X-Credits-Used: 41
X-Credit-Unit-USD: 0.0025
X-Credits-Cost-USD: 0.1025
X-Credits-Remaining: 99950
X-Credits-Remaining is a projection — your balance as read at the
start of the call, minus this call's cost. It is meant for a quick "how
much is left" read, not exact accounting: concurrent calls and the
moment a charge settles mean it can lag slightly. For an authoritative
balance, read the portal. The header is omitted when the balance could
not be read; the response still succeeds.
On a non-billable response, X-Billable is false, X-Credits-Used
and X-Credits-Cost-USD are 0, and no balance is projected.
When you run out — 402 PaymentRequired
When a call cannot be funded, it is refused with 402 before the
endpoint does any work, so an unfunded call never partially executes.
The body is the standard error envelope (see errors.md):
{
"timestamp": "2026-06-22T19:04:20.118Z",
"error": "PaymentRequired",
"message": "Insufficient credits",
"request_id": "ce9ea793-1759-4cad-ab1b-fbc11475df3a"
}
The message tells you which limit you hit:
message | Condition |
|---|---|
Insufficient credits | Your account balance is depleted. |
Key credit allocation exhausted | The calling key's per-key allocation is used up, even though the account balance is not. |
Account credit allocation exhausted | Your account-wide cap is reached. |
Branch on the error code (PaymentRequired), not the message text.
A 402 is not retriable — retrying the same call without changing
your funding gets the same 402. Resolve it by funding the balance or
raising the relevant budget in the portal, then retry. Because a 402
is a failed response, it is not billed.
Variable-cost endpoints
Most routes are fixed-cost: the same whole number of credits every call. A small number are variable-cost — their charge scales with the size of the call rather than being a flat per-route number.
Four routes are variable-cost, and they are the ones that send text to a model and return what it wrote:
POST /api/ask/runPOST /api/assist/summarizePOST /api/assist/chatPOST /api/extract/document
A short call costs less than a long one. All four price the same way:
in = input_tokens (or request bytes when usage is null — UTF-8 byte length)
out = output_tokens (or response bytes when usage is null)
credits = max(1, ceil(in × input_cost_factor + out × output_cost_factor))
The two per-unit prices — input_cost_factor and output_cost_factor — belong
to the model that served the call, so two models price the same text
differently, and a model can weight the tokens it writes differently from the
ones it reads. On /api/ask/run you choose that model with ?model=; on the
other three the engine chooses it (Assist lets you nudge it with tier), and
the response tells you which one it was under model. The current factors are
shown on the developer portal's pricing page.
On the pricing page these routes read "by model usage" rather than a number,
because there is no single per-call figure to show. As with every call, the
exact credits charged come back in X-Credits-Used — read that rather than
predicting the cost yourself.
The same rules above still apply: a variable-cost call is only billed when it succeeds, it is checked against your balance and budgets before it runs, and a floor of one credit means no successful call is ever free.
Add-on charges
An add-on is charged on top of an endpoint's own cost, when a call opts into a feature that carries a real per-use upstream fee. It is not a route and it does not appear in the per-endpoint price list; it sits under add-on charges on the developer portal's pricing page, and it is only ever charged to a call that asked for it.
There is one today:
| Add-on | Charged when | Unit |
|---|---|---|
| Web search | POST /api/assist/chat is sent with "web_search": true and the turn actually searches | Per search |
Two things to size for. It is per search, not per call — a single turn may
search more than once, and web_searches on the response says how many it ran.
And on a short call the add-on can dominate the bill many times over, because
it covers a per-query fee rather than tokens; setting the flag on every turn is
the expensive mistake. A turn that opts in and then does not need to search is
charged nothing extra.
What is and isn't billed
Billed:
- Every successful (
2xx/3xx) authenticated/api/*call. - Each established transcription session (flat per-session cost).
Not billed:
- Any failed call —
400,401,404,413,415,422,429, and every5xx. Fix the request and retry without spending credits. - A call refused with
402for insufficient funds. - The unauthenticated
GET /healthliveness probe.
Recommended client behavior
- Watch
X-Credits-Remaining. Surface low balances to your own operators before they hit zero, rather than discovering it on a402. - Treat
402as terminal, not retriable. Stop the call, alert, and resolve funding — do not loop. Looping on a402never clears it. - Distinguish
402from429. A429clears when its window rolls over; honorRetry-Afterand retry. A402clears only when you add funds or raise a budget. Don't apply rate-limit backoff to a402. - Read cost from the headers. If you need to attribute spend,
record
X-Credits-Used/X-Credits-Cost-USDper call rather than assuming a fixed price per route. - Don't burn credits on avoidable failures. Validation and de-identification errors are free, but they still consume rate-limit window — fix them client-side (see errors.md).
Related
- errors.md — the error envelope and the full
errorcode taxonomy, includingPaymentRequired. - rate-limits.md — the
X-RateLimit-*headers and the contrasting "every request counts" rule. - authentication.md — keys and how to send them.
- ask-run.md — the variable-cost model endpoint and its per-call credit formula.
Change history
| Date | Change |
|---|---|
| 2026-06-22 | Initial publication — credit metering, the X-Credits-* headers, 402 PaymentRequired, and per-key / account budgets. |
| 2026-06-26 | Added the Variable-cost endpoints section for /api/ask/run; clarified that most (not all) routes are fixed-cost. |
| 2026-06-26 | Variable-cost pricing split into per-model input_cost_factor / output_cost_factor (input and output units priced separately). |