Costs & billing
threadle shows two dollar figures and never conflates them.
| Figure | Means |
|---|---|
| tracked costs | The list-price value of the tokens — what an API key would have been billed |
| actual spend | What you really paid |
On a subscription those numbers are wildly different, and pretending otherwise is the most common way agent dashboards lie. A Max-plan week can show hundreds of dollars tracked and $0 (subscription) actual — both true.
Per provider
Section titled “Per provider”| Provider | tracked costs | actual spend |
|---|---|---|
| Claude Code | Computed from the transcript, per model, at models.dev prices | $0 on a subscription, = tracked on an API key |
| opencode | The cost opencode recorded itself | Same number — opencode reports what its own keys were billed |
| Cursor Agent | — (no list price in public storage) | $0 — Cursor Agent sessions are subscription-billed |
Cursor token counts come from the agent CLI result when threadle captured one, otherwise a chars ÷ 4 estimate. Estimated figures are prefixed ~ and carry a token source row (cli, turn_ended, or estimate (chars/4)).
The billing switch
Section titled “The billing switch”Settings → claude code billing:
| Setting | Effect on Claude sessions |
|---|---|
subscription (Pro / Max) — default | actual spend = $0, tracked stays as computed |
API key (pay per token) | actual spend = tracked |
Stored as claudeBilling in ~/.config/threadle/settings.json. When Claude Code has cached plan data locally, the settings card names the detected plan and billing type so the switch is not a guess.
Spend tripwires trip on tracked session dollars where a provider reports them — a measurement, not a forecast.
Usage by model
Section titled “Usage by model”Claude’s /cost breakdown, reproduced from the transcript. One pass tallies per model: input, output, cache read, cache write, and the 1h-TTL share of cache write. The session panel renders a row per model with its dollar figure and in · out · cache read · cache write.
Pricing a model means finding its exact models.dev id, or the longest key the id extends — dated snapshots like claude-sonnet-4-5-20250929 price off their base entry.
cost = ( input × in$ + output × out$ + cacheRead × cacheRead$ + write5m × cacheWrite$ + write1h × (in$ × 2) ) / 1_000_000models.dev’s cache_write price is the 5-minute rate. Cache writes with a 1-hour TTL bill at roughly 2× the input rate, so threadle splits cacheWrite into its 5m and 1h parts and prices them separately. Without an input price it falls back to 1.6 × the 5m rate.
A model with no price shows — for its row and contributes nothing to the total. If no model in the session can be priced, the session has no tracked cost at all rather than a silent $0.00.
Cache economics
Section titled “Cache economics”Cache hit rate is always:
cache read ÷ (fresh input + cache read + cache write)It appears per session, per model, per provider, and per project. Higher is cheaper: cache reads are billed at a fraction of fresh input, so a long session with a high hit rate can cost less than a short one that keeps rebuilding its prompt.
Duration and code changes
Section titled “Duration and code changes”| Row | Source |
|---|---|
| duration (API) | Sum of the transcript’s turn_duration system lines — agent working time, matching /cost’s “API duration” |
| duration (wall) | updated − created — elapsed clock time, including your thinking time |
| code changes | +N −M lines, summed from Edit patch hunks and new-file writes |
opencode sessions additionally carry their own diff summary (+N −M across K files).
Plan & utilization
Section titled “Plan & utilization”The claude code section of Statistics can show plan, plan · 5h window, and plan · 7d window tiles with utilization bars and reset times.
These are read passively from ~/.claude.json — the state file Claude Code maintains itself, including the plan tier from your OAuth account and the utilization it cached from its own usage calls. threadle never phones home for this; it mirrors what the CLI already stored locally, and the tile tooltip says how old that cache is. Email address and referral data present in the file are deliberately ignored.
No file, no account block, no tiles.
Where prices come from
Section titled “Where prices come from”List prices are the models.dev database — the same data opencode uses to compute its session costs.
Lookup is strictly local-first: any on-disk copy wins regardless of age.
~/.cache/opencode/models.json— opencode’s own mirror~/.config/threadle/models-pricing.json— threadle’s cache
The freshest of the two by mtime is used. The network is contacted once, only when no local copy exists anywhere, then cached to disk. A failed fetch backs off for an hour. Stale list prices beat surprise network calls.
⟳ fetch prices in Statistics forces a refresh now (POST /api/pricing/refresh) and replaces the cache. The source URL is configurable in Settings and must be http(s); it defaults to https://models.dev/api.json.
The pricing table is scoped to providers you can actually run: anthropic, opencode, and every provider prefix exposed by your opencode install. When nothing is available it says so — unavailable (offline and no local cache) — instead of showing zeros.
Related
Section titled “Related”- Timeline, diff & stats — where these numbers are displayed
- Session blueprint — per-session cost and cache rows
- Providers — what each adapter can and cannot report
- Tripwire — spend circuit breakers