# Pricing & billing

Pricing is pass-through: the per-token price you pay is the provider's price,
published per model in USD per million tokens. There is no per-token markup and
no hidden fee line. Every billed response carries `usage.cost` — the exact
amount your balance was debited, computed with the same integer math the ledger
settles with — so you can recompute your bill from the wire at any time. One provider
is our own: RouterPlus, which serves our own decision models on our own GPUs. Its models are
free: $0 in and out (see [Decision models](#decision-models)).

## Where prices live

The machine-readable catalog is public, no key required:

```bash
curl -s https://dev.routerplus.com/api/models.json \
  | jq '.models[] | select(.id == "claude-sonnet-4-5").pricing_usd_per_million'
```

```json
{
  "prompt": "3",
  "cached_prompt": "0.30",
  "cache_write": "3.75",
  "completion": "15"
}
```

Prices are **decimal strings** (up to six fractional digits), USD per one
million tokens. They convert exactly to the ledger's internal unit — no float
rounding is involved anywhere in billing.

| SKU | What it bills | Example (`claude-sonnet-4-5`) |
|---|---|---|
| `prompt` | non-cached input tokens | $3 / M |
| `cached_prompt` | prompt-cache **reads** | $0.30 / M |
| `cache_write` | prompt-cache **writes** | $3.75 / M |
| `completion` | output tokens (including reasoning, unless a distinct rate is declared) | $15 / M |
| `internal_reasoning` | reasoning subset of output, when a provider declares a distinct rate | — |

> [!NOTE]
> A model that does not declare a cache or reasoning SKU bills those tokens at
> its base rate: `cached_prompt` and `cache_write` default to the `prompt`
> price, `internal_reasoning` defaults to the `completion` price. A missing
> price never means free — that would be an accidental $0 SKU, and the ledger
> refuses to guess.

## Image models

Image models bill on the **same two SKUs**: `prompt` for the text you send in,
`completion` for the image that comes out, both in USD per million tokens.
OpenAI reports image output in tokens, and the count is fixed by the size and
quality you ask for. The per-image ceiling — the most output tokens one image
can bill — is `max_output_tokens` on the model page and in
`/api/models.json`.

| Model id | Text in /1M | Image out /1M | Per-image ceiling |
|---|---|---|---|
| `gpt-image-1` | $5 | $40 | 6,240 tokens |
| `gpt-image-1-mini` | $2 | $8 | 6,500 tokens |
| `gpt-image-1.5` | $5 | $32 | 6,500 tokens |
| `gpt-image-2` | $5 | $30 | 8,232 tokens |
| `gpt-image-2.5-flare` | $5 | $30 | 8,232 tokens |
| `gpt-image-2.5-sunburst` | $5 | $30 | 8,232 tokens |

Every output token bills at the image rate, including the text output tokens
`gpt-image-1.5` folds into its `output_tokens`. The full route contract is
[POST /v1/images/generations](/docs/api-images).

## Decision models

Decision models answer typed questions on [POST /v1/decisions](/docs/api-decisions). They
are priced like everything else: USD per million tokens.

| Model id | Input /1M | Cached /1M | Output /1M | What the token counts are |
|---|---|---|---|---|
| `typesafe/jev-1.13` | $0.042 | – | $0 | The provider's input count |
| `bespokelabs/nimble-v4` | $0.04 | – | $0 | The provider's input count |
| `bespokelabs/nimble-v4-max` | $0.09 | – | $0 | The provider's input count |
| `cloudflare/clef` | $0.24 | – | $0 | The provider's input count |
| `cloudflare/clef-flash` | $0.09 | – | $0 | The provider's input count |
| `routerplus/decider-2b` | $0 | $0 | $0 | The provider's input count; an answer from its cache bills $0 |
| `routerplus/kev-4b` | $0 | $0 | $0 | The provider's input count; an answer from its cache bills $0 |
| `perplexity/pplx-decider-v1-27b` | $0.04 | – | $0 | The provider's input count, which holds the state once per question |
| `openai/gpt-6-luna-decisions` | $0.10 | – | $0 | The provider's input count: the state once, and about 150 tokens for each question |
| `levanto/sage-1.2` | $0.05 | – | $0 | The provider's input count, which holds the state once per question |
| `scaledown/classify-1` | $0.05 | – | $0 | The provider's input count, summed over its questions (each one is a call) |
| `fastino/gliner-2.5-decide` | $0.03 | – | $0 | Fastino's `prompt_tokens` |

Bespoke's Nimble models are billed on Bespoke's own input count at Bespoke's own prices, with
no markup: $0.04 per million input tokens for Nimble v4, $0.09 for Nimble v4 Max, and
output is free. Bespoke counts the state and the questions once per call, not once per
question. A three-question call of 178 input tokens costs $0.000007 on Nimble v4 and
$0.000016 on Nimble v4 Max. `bespokelabs/nimble-latest`, which no list shows, is billed at
$0.04 per million input tokens, Bespoke's price for `nimble-latest`, whichever model Bespoke
picks for it.

Clef and Clef-flash are billed on Cloudflare's own input count at Cloudflare's own price,
with no markup: $0.24 and $0.09 per million input tokens, and output is free (Cloudflare
reports `output_tokens: 0`). Cloudflare bills Workers AI in neurons, at $0.011 per 1,000
neurons. Its price page lists Clef at 21,818 neurons ($0.24) and Clef-flash at 8,182 neurons
($0.09) per million input tokens. A call of 400 input tokens costs $0.000096 on Clef and
$0.000036 on Clef-flash.

Decider 2B and Kev 4B are RouterPlus's own models, on our own GPUs, not an outside
provider's. So their price is ours to set, not a provider's price passed through, and it is
$0: input, cached input and output are all free, and `usage.cost` is `0` on every call.
RouterPlus still reports its input count, which is at most the model's context: a long input
that Decider 2B (25,600 tokens) cuts counts only the tokens the model read. A free call is
still metered, counted against your limits and its pool, and recorded in
`GET /v1/generation`, and it still needs an organization with credit. RouterPlus answers an
identical request from its cache for 600 s.

Perplexity Decider v1 27B is billed on Perplexity's own input count at Perplexity's own
price, with no markup: $0.04 per million input tokens, and output is free. Unlike Bespoke,
Perplexity runs one prompt for each question, each with the whole state, and counts the
state once per question. So a call costs about one state for each question it asks: five
questions about a state of 1,843 tokens bill 9,215 input tokens, $0.000368. One call with
many questions still saves round trips.

GPT-6 Luna Decisions is billed on OpenAI's own input count at OpenAI's own price, with no
markup: $0.10 per million input tokens, and output is free (OpenAI reports
`output_tokens: 0`). It is billed at its list price: no discount applies to it today. OpenAI
bills the state once per call, but it runs a prompt for each question, so each question adds
about 150 input tokens: one yes/no about a two-character state billed 156 tokens, and five
billed 760. A three-question call (a choice, a score and a yes/no about a short complaint)
billed 396 input tokens, $0.000039. So one call with many questions saves round trips and
the state's tokens, but not the questions' tokens. A question that OpenAI declines (its
answer is `{"type": "refusal"}`) is billed too. The listing's context is 272,000 tokens: up
to there OpenAI's listed price holds, and OpenAI bills input above it at twice the rate.

GLiNER-2.5-Decide is billed on Fastino's own input count: $0.03 per million input tokens,
and output is free. `usage.input_tokens` is Fastino's `prompt_tokens`, and
`usage.output_tokens` its `completion_tokens`. A call of 1,000 input tokens costs $0.00003.

Sage is billed on Levanto's own input count at Levanto's own price, with no markup: $0.05
per million input tokens, and output is free. Like Perplexity Decider, it reads and bills the
state once per question, so a call costs about one state for each question it asks.

### The decisions discount

A discount may apply to decision models: a percent taken off the charge of every decision
model, or of some of them, each at its own percent, from every key, the playground's
included. It does not change the list price, and it can change or end. A discounted model's
page and its row in the directory show the percent beside the struck list price, and its row
in `/api/models.json` carries `discount_percent`. Every discounted response says what
applied:

| Where | What it shows |
|---|---|
| `usage.cost` | What you paid, after the discount |
| `usage.cost_before_discount` | The charge at the list price |
| `usage.discount_percent` and the `x-tm-discount-percent` header | The percent taken off |
| `cost_usd` in [GET /v1/generation](/docs/api-usage) | What you paid, after the discount |
| `inference_cost_usd` in GET /v1/generation | The cost at the list price |

The discounted charge is the list charge × (100 − percent) / 100, rounded down to the
micro-dollar. When no discount applies, the three discount fields are absent and
`usage.cost` is the list charge. A discount is a lower charge, not a markup: pricing stays
pass-through. A discount is for organizations with credit: with a balance of $0 a
discounted request is refused with `429 insufficient_quota`, as any request is, even at
100 %.

When a discount covers a model whose list price is already $0 (Decider 2B and Kev 4B today), the discount has nothing to
take off: `usage.cost` and `usage.cost_before_discount` are both `0`. The gateway still
reports `usage.discount_percent` and the header on it, as on every model the discount
covers, and `/api/models.json` still carries `discount_percent` on its row, so one reader works for all
twelve models. The model's page, the directory and the Playground show no "N % off" beside a
$0 price: the model is free, not discounted.

## The math: integer micro-USD

All money math is integer micro-USD (millionths of a dollar). A published price
like `"2.50"` becomes exactly `2500000` micro-USD per million tokens. Two
rounding rules, both fixed:

- **Reserve rounds up** (ceiling) — the pre-flight hold is conservative.
- **Settlement rounds down** (floor) — the actual charge, in your favor.

The settlement formula, verbatim from the ledger:

```
non_cached_input = max(0, input_tokens − cached_tokens − cache_write_tokens)
reasoning        = clamp(reasoning_tokens, 0, output_tokens)
plain_output     = output_tokens − reasoning

cost_micro = floor((
    non_cached_input   × prompt_per_M
  + cached_tokens      × cached_prompt_per_M
  + cache_write_tokens × cache_write_per_M
  + plain_output       × completion_per_M
  + reasoning          × reasoning_per_M
) / 1,000,000)
```

where each `*_per_M` is the integer micro-USD price. `usage.cost` on the wire
is this number divided by 10⁶.

## Reserve, then settle

Every billed request runs the same lifecycle:

1. **Reserve.** Before any provider is contacted, the gateway holds a
   conservative estimate of the cost. On the chat surfaces the input is estimated at one
   token per byte of the request body, plus 65,536 tokens for each image or
   document part (an image sent inline counts the 65,536 only, not its base64
   bytes), and the output at your `max_tokens` (or
   `max_completion_tokens`, or `max_output_tokens` on
   [POST /v1/responses](/docs/api-responses)), all of it. When you send no
   output limit, the output is held at 32,768 tokens at most, although the
   model gets its own maximum: on the chat surfaces the gateway writes that
   maximum into the request, lowered when needed so that the input and the
   output fit the model's context window (see
   [POST /v1/chat/completions](/docs/api-chat-completions)); on
   `/v1/responses` it adds none, and OpenAI applies the same maximum.
   Each side is
   priced at the model's most expensive rate for that side — cache writes and
   reasoning when they cost more than plain input or output — and the sum is
   rounded **up** to micro-USD. A request with a web search tool also holds the
   search fee for the tool's `max_uses` searches (at most 20), or for 5 when it
   sets none (see [Web search](#web-search)). On `/v1/images/generations` the output is
   reserved at `n` × the model's per-image ceiling (not `max_tokens`), and the
   input at the prompt bytes ÷ 4.
2. **Dispatch.** The request goes to a provider. Prices are pinned at this
   moment (see [frozen snapshots](#price-changes-frozen-snapshots)).
3. **Settle.** When the attempt finishes, the provider-reported token counts
   are priced with the formula above, rounded **down**. That is what you pay,
   also when it is more than the hold (a long answer to a request without a
   limit).
4. **Release.** The hold is released in full, and you pay the settled
   amount. An attempt can end before the provider reports its usage: you
   cancelled it, or the provider failed after your answer started. Then you pay
   for the input and for the output already streamed to you, never the hold:
   - **Input:** the provider's own count when it sent one (Claude sends it
     when its answer starts, with its cache reads and writes, each at its own
     price). Otherwise the request's likely input — its size at four bytes a
     token, with 1,600 tokens for each image or document part — at the plain
     input price.
   - **Output:** what was streamed to you before the end (text, tool-call
     arguments and any reasoning you received), at four characters a token.
     If you cancel before the first token, or the call does not stream, the
     output part is $0.

   A model that hides its thinking (the Claude 5 models, by default) streams
   nothing while it thinks, so a cancel during that time costs the input only.
   Such a charge has the provenance `estimated`. You are charged the reserved
   maximum only when the gateway itself stopped mid-request and cannot observe
   the outcome (`unknown_after_crash` in
   [GET /v1/usage & /v1/generation](/docs/api-usage)), because the call may
   have been billed upstream, and in the rare case of an attempt that completes
   without the provider reporting any usage.

A reservation can be refused before dispatch:

- **Balance too low** — your balance must cover every reservation your org has
  in flight, including this one. Refusal is a `429` with
  `error_type: insufficient_quota` (the OpenAI-native shape SDKs already
  handle; `billing_error` native type on the Anthropic surface).
- **Monthly spend cap hit** — hard caps (org-wide and per-key, UTC calendar
  month, strictest wins) return the same `429 insufficient_quota` with the
  exact reset time in the message and the `x-tm-cap-reset` header.
- **A rate limit hit** — the key's RPM, a shared limit of the organization, or
  the provider's pool: `429` with `error_type: rate_limit` and `retry-after`.

Remediation for each is in [Errors & remediation](/docs/errors).

### All-or-nothing on images

An image generation either arrives whole or costs nothing. A failed attempt, a
response over the 32 MiB cap, and a 200 that carries no decodable image all
settle at zero — there is no partial image and no partial bill. The one nuance:
if you hang up mid-render the render still finishes upstream, so that attempt
settles the **provider-reported** usage, never the reservation and never a
pretend $0.

## The cost on the wire: `usage.cost`

Every billed response carries `usage.cost` (USD, a JSON number):

- **Non-streaming** — inline in the response body's `usage`, both surfaces.
- **Streaming, OpenAI surface** — in the final usage chunk, before `[DONE]`.
- **Streaming, Anthropic surface** — in the `message_delta` usage at stream end.

```bash
curl -s https://dev-api.routerplus.com/v1/chat/completions \
  -H "Authorization: Bearer $TM_API_KEY" -H "content-type: application/json" \
  -d '{"model":"claude-sonnet-4-5","max_tokens":350,"messages":[{"role":"user","content":"hello"}]}' \
  | jq .usage
```

```json
{
  "prompt_tokens": 1200,
  "completion_tokens": 350,
  "total_tokens": 1550,
  "prompt_tokens_details": { "cached_tokens": 800, "cache_write_tokens": 0 },
  "cost": 0.00669
}
```

```python
import os
from openai import OpenAI

client = OpenAI(base_url="https://dev-api.routerplus.com/v1", api_key=os.environ["TM_API_KEY"])
r = client.chat.completions.create(
    model="claude-sonnet-4-5", max_tokens=350,
    messages=[{"role": "user", "content": "hello"}],
)
print(r.usage.model_dump()["cost"])  # the exact ledger debit, in USD
```

```python
import os
import anthropic

client = anthropic.Anthropic(base_url="https://dev-api.routerplus.com", api_key=os.environ["TM_API_KEY"])
msg = client.messages.create(
    model="claude-sonnet-4-5", max_tokens=350,
    messages=[{"role": "user", "content": "hello"}],
)
print(msg.usage.model_dump()["cost"])
```

> [!WARNING]
> `usage.cost` is the **full request debit**, not the serving attempt's slice.
> If a provider failed mid-prompt and the gateway failed over before your
> answer started, any tokens the failed attempt consumed are included in the
> number you see. The per-attempt breakdown is always available at
> [GET /v1/generation](/docs/api-usage) — nothing is hidden in an average.

One documented limitation: an Anthropic-surface **stream that dies after your
answer started** has no legal wire slot for usage outside `message_delta`, so
its terminal error event carries no cost. Recompute those from
[/v1/generation](/docs/api-usage). (The OpenAI surface emits any known
usage+cost before its terminal error, so its wire stays complete.)

Usage reporting is always on: streams and non-streams both carry it, and the
gateway requests usage upstream regardless of what you send, because
unreported usage would be an unrecomputable bill. The one `stream_options` value
that changes anything is an explicit `include_usage: false`: you are billed the
same, but no usage chunk is written to you.

## Cache reads and writes

Cache tokens are billed at their own rates on **both** surfaces, and both
surfaces report the split:

| Concept | OpenAI surface | Anthropic surface | Billed at |
|---|---|---|---|
| Non-cached input | `prompt_tokens` minus the two details below | `input_tokens` | `prompt` |
| Cache reads | `prompt_tokens_details.cached_tokens` | `cache_read_input_tokens` | `cached_prompt` |
| Cache writes | `prompt_tokens_details.cache_write_tokens` | `cache_creation_input_tokens` | `cache_write` |
| Output | `completion_tokens` | `output_tokens` | `completion` |

On the OpenAI surface `prompt_tokens` is cache-inclusive (reads and writes are
inside it), and billed responses always carry `prompt_tokens_details` with both
fields — zero-filled when the upstream reports none — so stream and non-stream
usage share one recomputable shape.

On Claude, a cache write costs 1.25 times the input price (`cache_write`), and a
cache read costs a tenth of it or less (`cached_prompt`). When an agent sends a
conversation with tools or earlier turns and no cache marks of its own, the
gateway turns on Claude's automatic caching for it. The first turn then pays a
cache write on its prompt, and the next turns read that prompt at the cache
price. The `x-tm-auto-cache` response header says whether it was applied, and
the request header `x-tm-auto-cache: off` turns it off. The rules are on
[Wire compatibility](/docs/compat#automatic-caching-on-claude).

## Web search

A web search that the provider runs for the model is billed at the provider's
price per search, in addition to the tokens:

| Tool | Where | Price |
|---|---|---|
| Anthropic's web search server tool (`web_search_20250305` and later) | Claude models on `POST /v1/messages` | **$10 per 1,000 searches** ($0.01 a search) |
| Anthropic's web fetch server tool | Claude models on `POST /v1/messages` | no fee: only the tokens of the fetched page |
| OpenAI's hosted `web_search` tool | OpenAI models on [POST /v1/responses](/docs/api-responses) | $10 per 1,000 calls |
| OpenAI's older `web_search_preview` tool | GPT-4o, GPT-4o mini and GPT-4.1 mini on `POST /v1/responses` | $25 per 1,000 calls |

Each search counts once, whatever the number of results, and a search that
fails is not billed. The pages a search returns become input tokens, billed at
the model's input price, as on the provider's own API; on a stream, too, the
input is the count at the end of the turn.

- The fee is part of `usage.cost`, and of `cost_usd` in
  [GET /v1/usage & /v1/generation](/docs/api-usage), where each attempt also
  shows `server_tool_use` (`web_search_requests`, `web_fetch_requests`) and
  `server_tool_fee_usd`. On `/v1/messages` the number of searches is in the
  answer's `usage.server_tool_use.web_search_requests` too.
- While the request runs, the hold includes the fee for each web search tool's
  `max_uses` searches (at most 20), or for 5 searches when the tool sets no
  `max_uses`. A tool of your own that happens to be named `web_search` holds
  nothing. If the model searches more often than the hold allows, you pay for
  every search it made: the hold bounds what is set aside, never the charge.
- On your own provider connection (BYOK) there is no fee from us: the provider
  bills you directly.

Anthropic's server tools, web search included, work only for Claude models on
`POST /v1/messages` (Claude Code calls it there). See
[POST /v1/messages](/docs/api-messages).

## Reasoning tokens

Reasoning tokens are a **subset of output**, never billed on top of it. The
settle math splits them out and prices them at the model's reasoning rate;
today every cataloged model prices reasoning equal to `completion`, so the
split is billing-neutral until a provider declares a distinct rate. The OpenAI
surface reports them in `completion_tokens_details.reasoning_tokens`; the
Anthropic wire does not report them separately.

## Price changes: frozen snapshots

The price you pay is the price at **dispatch time**. Price rows are
append-only — a change is a new effective-dated row, never an edit — and the
row in effect when your request is reserved is pinned into the reservation and
written onto every attempt's ledger row. A price change published while your
request is in flight cannot touch it, and historical attempts always settle
(and audit) against the prices they were dispatched under.

## Paid credits and purchase matching

Create an account at [Sign up](https://dev.routerplus.com/signup), complete Clerk authentication
and email verification, and save your first API key. Signup and card verification
alone do not grant credit. Existing customers keep their balance and API keys.

Standard credit purchases start at **$10** in [Billing](https://dev.routerplus.com/console/billing).
The payment adds the amount you buy as paid credit. Your balance is the sum of
credit ledger entries minus settled spend.

If purchase matching is offered for your account, Billing shows the cap and remaining matching credit.

Only eligible paid purchases receive a match, when the offer is available in Billing.

New matching offers have a lifetime maximum of **$100 per account** across eligible
purchases. Billing shows the offer and remaining matching credit for your account.
Matching credit is promotional and appears separately in the ledger. Refunds and chargebacks
reverse the corresponding match; a partial refund reverses its proportional
matching credit. Adding a card without a
purchase does not earn a bonus. Existing trial and card-verification grants stay
in the ledger, but those signup offers are no longer available.

Before an organization buys credit, its customer keys share the trial rate of
20 requests/minute. A purchase lifts that cap; each key then has its own ceiling
(300 for a console key). Historical trial credit can also be limited when unusual
usage is detected; contact support if that happens.

## Worked example

A `claude-sonnet-4-5` request reports 1,200 prompt tokens (800 of them cache
reads, no cache writes) and 350 completion tokens:

```
non-cached input:   400 × 3,000,000  = 1,200,000,000
cache reads:        800 ×   300,000  =   240,000,000
output:             350 × 15,000,000 = 5,250,000,000
                                       ─────────────
sum / 1,000,000 (floor)              = 6,690 micro-USD
```

`usage.cost` on the wire: `0.00669`. The same number appears as the attempt's
`cost_usd` in [GET /v1/usage & /v1/generation](/docs/api-usage), because they
are the same computation on the same integers.

## Worked example: an image

A `gpt-image-1` request for two medium 1024×1024 images (`n: 2`) with a
12-token prompt estimate. Text in is $5 / M, image out is $40 / M, and the
per-image ceiling is 6,240 tokens.

The reservation, before any provider is contacted:

```
prompt estimate:  12 ×  5,000,000 =         60,000,000
output ceiling:   2 × 6,240 × 40,000,000 = 499,200,000,000
                                           ───────────────
ceil(sum / 1,000,000)                    = 499,260 micro-USD
```

The provider reports 12 input tokens and 2,112 output tokens (1,056 per image),
so settlement is:

```
input:            12 ×  5,000,000 =         60,000,000
output:        2,112 × 40,000,000 =     84,480,000,000
                                        ──────────────
floor(sum / 1,000,000)            =     84,540 micro-USD
```

`usage.cost` on the wire: `0.08454`. The hold is released in full; you pay the
settled number, not the reservation.
