Skip to content

Model prices

Transcripts record tokens, not money. spwn gets to a dollar figure by multiplying each session’s tokens by a rate table, and you control that table. The table ships with Anthropic’s published rates. Edit it for negotiated pricing, add a model spwn doesn’t know yet, or empty it to track tokens only.

Open Settings → Model prices. Changes take effect when you press Save.

Each row is a model id and five rates, in USD per million tokens:

Column Field in settings.json Bills Usual ratio
Input input Uncached input tokens base
Output output Output tokens
5m write cacheWrite5m Cache writes with the 5-minute TTL 1.25× input
1h write cacheWrite1h Cache writes with the 1-hour TTL 2× input
Cache read cacheRead Cache hits 0.1× input (0.025× on Fable/Mythos 5.1)

A session’s usd is the sum over its models of each token count times its rate, divided by a million, rounded to four decimals. The two cache-write TTLs are priced separately because they bill differently, and in practice the 1-hour rate is where most cache-write tokens land.

The table is stored in settings.json under modelPrices:

{
"modelPrices": {
"claude-opus-5": {
"input": 5.0,
"output": 25.0,
"cacheWrite5m": 6.25,
"cacheWrite1h": 10.0,
"cacheRead": 0.5
},
"claude-opus-5#fast": {
"input": 10.0,
"output": 50.0,
"cacheWrite5m": 12.5,
"cacheWrite1h": 20.0,
"cacheRead": 1.0
}
}
}

A model id in the table matches any model whose id starts with it, and the longest match wins. That way one row covers every dated release:

Model in the transcript Priced by
claude-opus-4-5-20251101 claude-opus-4-5, not the older and pricier claude-opus-4
claude-haiku-4-5-20251001 claude-haiku-4-5
claude-fable-5-1 claude-fable-5-1, not claude-fable-5

To price one dated release differently, add a row with its full id. The longer key wins.

When a response reports speed: "fast", spwn records it under the model id plus a #fast suffix, for example claude-opus-5#fast. Fast mode bills at its own rate, which is twice the standard rate on Opus 5 and Opus 4.8. The published table has #fast rows for those two models.

Because matching is by prefix, a #fast id with no #fast row of its own falls back to the plain model’s row, and is priced at the standard rate. Add a #fast row to price it correctly.

A model that no row matches is unpriced. Its tokens and requests are still counted, but it adds nothing to usd, and its id is listed under unpriced in the doc:

  • If no model in a session had a rate, there’s no usd key at all. The chips show a request count (58 req) instead of a dollar figure, and the PR tab says Tokens only.
  • If some models had rates, usd covers those, and the PR tab notes which models it excludes.

spwn reports a missing price as missing, never as $0.00. A row whose rates are all zero is different: it prices that model as free, and the session gets usd = 0.

Restore published rates puts back the table spwn ships with. It saves right away, along with any other unsaved changes on the Settings page. The published rates live in one place, backend/src/pricing.rs. Restoring saves settings with modelPrices left out, and the backend fills in its own defaults, so the UI never holds a second copy that could drift.

The same rule applies to settings.json: if the modelPrices key is missing, you get the published rates. If it’s present, spwn uses exactly what it says.

On a Max or Team plan a turn costs nothing at the margin, so a dollar figure is misleading. Press Empty the table, then Save. The section header shows a tokens only chip, and no session gets a dollar figure. Every token and request count is still recorded, the chips show request counts, and fork and rollup figures work the same way.

In settings.json that’s:

{ "modelPrices": {} }

An empty table stays empty. spwn only fills in defaults when the key is missing, not when it’s {}.

Prices are applied when cost is written, which happens once per turn. After you save a new rate:

  • Each session’s next turn reprices its whole conversation at the new rates.
  • Figures already committed to a branch keep the rates they were written with. The history doesn’t change.
  • Spend across this problem in the PR tab and session.cost() in workflows price their group totals at the table as it is when you ask.

These are the rates spwn ships with (USD per million tokens):

Model id Input Output 5m write 1h write Cache read
claude-fable-5-1, claude-mythos-5-1 10 50 12.50 20 0.25
claude-fable-5, claude-mythos-5 10 50 12.50 20 1.00
claude-opus-5, claude-opus-4-8, -4-7, -4-6, -4-5 5 25 6.25 10 0.50
claude-opus-5#fast, claude-opus-4-8#fast 10 50 12.50 20 1.00
claude-opus-4-1, claude-opus-4 15 75 18.75 30 1.50
claude-sonnet-5 2 10 2.50 4 0.20
claude-sonnet-4-6, -4-5, claude-sonnet-4 3 15 3.75 6 0.30
claude-haiku-4-5 1 5 1.25 2 0.10
claude-haiku-3-5 0.80 4 1.00 1.60 0.08

Retired models stay in the table because an old branch can still carry their usage. If Anthropic changes its pricing before spwn ships an update, edit the rows yourself.