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 |
What “what a session cost” means for you
Section titled “What “what a session cost” means for you”| How you pay | What the UI emphasizes |
|---|---|
| Claude Pro / Max (subscription) | Tracked can look large (list-price tokens). Actual spend shows $0 (subscription). Both are true: you already paid the plan. Tracked is for comparing sessions and circuit breakers. |
| Claude API key | Flip Settings → claude code billing to API key. Actual spend = tracked. |
| opencode | Usually real API-key money. Tracked and actual are the same number opencode recorded. |
| Cursor Agent | Plan from agent about (subscriptionTier, e.g. Pro). Actual spend $0 on that subscription. Token counts may be estimated (~) when the CLI did not report usage. |
| Antigravity | Auth mode from ~/.gemini/settings.json (oauth-personal vs API/Vertex) and consumer/enterprise onboarding, not Gemini Advanced / Google One tier. Actual spend $0 on Google-account login. |
| Codex | ChatGPT plan type from ~/.codex/auth.json (JWT claims) when logged in via ChatGPT. API-key mode when auth_mode / key says so. Public rollouts still have no list-price usage, so tracked costs stay empty. Actual spend is $0 on a ChatGPT plan. |
| GitHub Copilot | Token counts from assistant_usage_events in session-store.db when present. Actual spend $0 on a Copilot subscription. |
| Grok Build | Tokens and tracked cost from each session’s usage.json (costUsdTicks). Actual spend $0 under SuperGrok. |
On a Max-plan week you can see hundreds of dollars tracked and $0 actual. Pretending those are one number is the most common way agent dashboards lie.
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 when agent about reports a subscription tier (Pro/Business/…) |
| Antigravity | (no list price in public storage) | $0 when ~/.gemini shows Google OAuth (oauth-personal / consumer). API/Vertex key mode is labeled when selected |
| Codex | (no list price harvested from rollouts yet) | $0 when ~/.codex/auth.json shows a ChatGPT plan (Plus/Pro/…). API-key mode is labeled but still has no per-token dollars from disk |
| GitHub Copilot | (events may carry token columns. No list-price dollars) | $0 on a Copilot subscription |
| Grok Build | From usage.json ticks (costUsdTicks / 1e10) | $0 under SuperGrok |
Cursor, Antigravity, Codex, Copilot, and Grok token counts come from the CLI result or provider storage when threadle captured one, otherwise a chars ÷ 4 estimate. Estimated figures are prefixed ~ and carry a token source of estimate. Plan / auth mode: Claude from ~/.claude.json, Cursor from agent about, Codex from ~/.codex/auth.json, Antigravity auth mode from ~/.gemini (no Gemini Advanced tier on disk).
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 circuit breakers 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, plan · 7d window, and plan · Opus 7d (when Claude cached that window) 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.
| Provider | What threadle can show |
|---|---|
| Claude Code | 5h + 7d (weekly) plan windows (+ Opus 7d when present). Agent cards chip when ≥90%. Runs preflight / fail at 100%. |
| Cursor Agent | Plan tier only (agent about). Model pools are monthly (Spending dashboard). Not mirrored as %. Exhaustion surfaces from CLI error text in the run log. |
| Grok / others | No remaining-% on disk. Same run-log classification when the CLI says so. |
No file, no account block, no tiles.
Where prices come from
Section titled “Where prices come from”List prices use the models.dev shape. The same data opencode uses for session costs. Runtime never fetches them. A vetted snapshot ships with threadle. CI refreshes it on a schedule and opens a PR.
Lookup order:
~/.cache/opencode/models.json. opencode’s own mirror (when present)- Bundled
models-pricing.jsonshipped with the package ~/.config/threadle/models-pricing.json. Legacy leftover from older releases (optional. Safe to clear)
Statistics shows the active source next to the table (opencode cache, bundled, or local cache). Maintainers refresh the bundle with node scripts/update-models-pricing.mjs (GitHub workflow update-models-pricing.yml).
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 (no bundled or local prices). 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
- Circuit breakers. Spend circuit breakers