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.
The rate fields
Section titled “The rate fields”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 } }}Longest-prefix matching
Section titled “Longest-prefix matching”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.
Fast mode
Section titled “Fast mode”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.
Models with no price
Section titled “Models with no price”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
usdkey 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,
usdcovers 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
Section titled “Restore published rates”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.
Tokens only, for subscription plans
Section titled “Tokens only, for subscription plans”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 {}.
When a change applies
Section titled “When a change applies”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.
Published rates
Section titled “Published rates”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.