Skip to content

Costs & billing

threadle shows two dollar figures and never conflates them.

FigureMeans
tracked costsThe list-price value of the tokens. What an API key would have been billed
actual spendWhat you really paid

What “what a session cost” means for you

Section titled “What “what a session cost” means for you”
How you payWhat 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 keyFlip Settings → claude code billing to API key. Actual spend = tracked.
opencodeUsually real API-key money. Tracked and actual are the same number opencode recorded.
Cursor AgentPlan 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.
AntigravityAuth 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.
CodexChatGPT 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 CopilotToken counts from assistant_usage_events in session-store.db when present. Actual spend $0 on a Copilot subscription.
Grok BuildTokens 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.


Providertracked costsactual spend
Claude CodeComputed from the transcript, per model, at models.dev prices$0 on a subscription, = tracked on an API key
opencodeThe cost opencode recorded itselfSame 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 BuildFrom 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).


Settings → claude code billing:

SettingEffect on Claude sessions
subscription (Pro / Max). Defaultactual 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.


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_000

models.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 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.


RowSource
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).


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.

ProviderWhat threadle can show
Claude Code5h + 7d (weekly) plan windows (+ Opus 7d when present). Agent cards chip when ≥90%. Runs preflight / fail at 100%.
Cursor AgentPlan tier only (agent about). Model pools are monthly (Spending dashboard). Not mirrored as %. Exhaustion surfaces from CLI error text in the run log.
Grok / othersNo remaining-% on disk. Same run-log classification when the CLI says so.

No file, no account block, no tiles.

Related: Agents · Errors.


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:

  1. ~/.cache/opencode/models.json. opencode’s own mirror (when present)
  2. Bundled models-pricing.json shipped with the package
  3. ~/.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.