How to track Claude Code token usage and cost

Claude Code writes a complete record of every session to your local disk, including token counts per turn. Nothing stops you from turning that into a dollar figure. Most people just do not know the files are there, or price them wrong once they find them.

By the Continuum team. We build a workbench that runs Claude Code, Codex, and their peers, so the model rates quoted here are the ones our own cost analytics ship with.

The short version

Claude Code logs each session as JSONL under ~/.claude/projects/, with input, output, cache-write, and cache-read token counts on every assistant turn. Codex does the same under ~/.codex/sessions/. The four counts price at different multipliers (cache reads at 0.1x base input, 5-minute cache writes at 1.25x, 1-hour writes at 2x), so summing them at one rate is the classic mistake. Five methods get you a number: /usage in-session, a status line, ccusage, OpenTelemetry export, and the vendor analytics APIs.

What you need to know
  • Session history lives at ~/.claude/projects/ for Claude Code and ~/.codex/sessions/ for Codex. Plain local files, not telemetry.
  • Each assistant turn records input, output, cache-write, and cache-read tokens separately. They price at different multipliers.
  • Cache reads are 0.1x the base input rate. They usually dominate the token count and barely register on the bill.
  • Deduplicate on message ID plus request ID or retried turns quietly inflate your totals.
  • On a subscription the dollar figure is notional: it is what the work would have cost on the API, which is the number that tells you whether the plan is good value.
  • ccusage is the reference open-source CLI, and it now covers Codex and a dozen other agents, not just Claude.

Five ways to get a number

Pick by what you actually need. Most people want the third row and reach for the first two by accident.

Claude Code cost-tracking methods, August 2026.

MethodScopeEffortGood for
/usage in a sessionThis machine, current session plus plan barsZeroAm I about to get cut off, and what is eating the window
Custom status lineLive, continuous, current sessionOne shell scriptWatching cost and context while you work
ccusageAll local history, all providersOne npx commandDaily, weekly, monthly, per-session, per-window totals
OpenTelemetry exportPer user, near real time, any providerA collector to runTeam dashboards across Bedrock, Vertex, or Foundry
Vendor analytics APIPer user, org-wide, authoritativeAn admin keyFinance and per-seat reporting

Where the data lives

Both major CLIs keep a full local transcript. This is not telemetry sent somewhere, it is a file on your machine.

Finding your session history
# Claude Code: one directory per project, JSONL per session
ls ~/.claude/projects/
# -Users-you-code-myapp/  -Users-you-code-otherapp/

# Codex: flat directory of rollout files
ls ~/.codex/sessions/

# How much history have you accumulated?
du -sh ~/.claude/projects ~/.codex/sessions

# A second Claude account lives wherever CLAUDE_CONFIG_DIR pointed
ls "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/projects"

One more trap worth knowing before you write anything: if you run a second Claude account under its own CLAUDE_CONFIG_DIR, its history is not under ~/.claude. Any tool that hardcodes the default path silently under-reports your real spend by whatever the second account did.

Reading the token counts

share of TOKENSshare of COSTcache read82%9%cache write9%18%input6%26%output3%47%Pricing all four buckets at one rate is why homemade estimates come out absurd.
Count and cost are not the same shape. Cache reads dominate the token count and barely register on the bill; output tokens are the reverse. Illustrative proportions from a long agent session.

Every assistant message in the JSONL carries a usage object. The four counts bill at different multipliers, so summing them into one number gives you a wrong answer, usually by an order of magnitude.

The shape of a usage record (trimmed)
{
  "type": "assistant",
  "message": {
    "id": "msg_01ABC...",
    "model": "claude-sonnet-5",
    "usage": {
      "input_tokens": 1204,
      "output_tokens": 892,
      "cache_creation_input_tokens": 18340,
      "cache_read_input_tokens": 96210
    }
  },
  "requestId": "req_01XYZ..."
}

Multipliers relative to the model's base input rate, from the Claude platform pricing page, checked 7 August 2026.

CountWhat it isMultiplier
input_tokensFresh prompt tokens1x base input
output_tokensWhat the model generatedBase output rate, typically 5x input
cache_creation_input_tokensTokens written into the prompt cache1.25x for a 5-minute cache, 2x for a 1-hour cache
cache_read_input_tokensTokens served from cache0.1x base input

The JSONL does not tell you which cache duration was used, so a parser has to pick a convention. Most tools assume the 5-minute write at 1.25x, which is right for interactive sessions on the default. If you set 1-hour caching deliberately, your real cost is higher than any generic tool will report.

A working calculation

Here is a minimal version that gets the shape right. It is not production quality, but it produces a defensible number in a few seconds.

01

Group the raw counts by model

cat ~/.claude/projects/*/*.jsonl \
  | jq -s '
      map(select(.type == "assistant" and .message.usage))
      | unique_by(.message.id + ":" + (.requestId // ""))
      | map({
          model: .message.model,
          i:  .message.usage.input_tokens,
          o:  .message.usage.output_tokens,
          cw: (.message.usage.cache_creation_input_tokens // 0),
          cr: (.message.usage.cache_read_input_tokens // 0)
        })
      | group_by(.model)
      | map({
          model:  .[0].model,
          input:  (map(.i)  | add),
          output: (map(.o)  | add),
          cacheW: (map(.cw) | add),
          cacheR: (map(.cr) | add)
        })
    '
02

Apply the per-bucket rates

As of August 2026: Opus 5 is $5 per million input and $25 output, Sonnet 5 is $2 and $10 on introductory pricing through 31 August 2026 (then $3 and $15), Haiku 4.5 is $1 and $5. Cache writes are 1.25x input, cache reads 0.1x input.

cost = (input  * in_rate
      + output * out_rate
      + cacheW * in_rate * 1.25
      + cacheR * in_rate * 0.10) / 1e6
03

Price each event at its own timestamp

Rates change. Sonnet 5 alone moves on 1 September 2026. If you reprice your whole history at today's rate, every historical total silently shifts. Serious tools keep dated rate windows and resolve each event against the rate that was live when it happened.

The deduplication trap

Retried and streamed turns can appear more than once in the transcript. Deduplicate on message ID plus request ID before summing, or your totals drift upward over time in a way that is very hard to notice, because nothing ever looks obviously wrong.

How many duplicates are actually in there?
cat ~/.claude/projects/*/*.jsonl \
  | jq -s 'map(select(.message.usage))
           | { raw: length,
               deduped: (unique_by(.message.id + ":" + (.requestId // "")) | length) }'

Using ccusage

You do not have to write this yourself. ccusage by ryoppippi is the established open-source tool for exactly this. It handles the path encoding, the dedup, and the pricing table, and it now reports across a long list of agent CLIs, not only Claude.

ccusage basics
npx ccusage@latest daily      # spend per day
npx ccusage@latest weekly     # roll up by week
npx ccusage@latest monthly    # roll up by month
npx ccusage@latest session    # per session
npx ccusage@latest blocks     # by 5-hour billing window

# Per-source reports: codex, opencode, gemini, copilot and more
npx ccusage@latest codex daily

When you need per-user numbers instead

Local parsing answers "what did this machine do". For a team you need attribution, and there are three official routes depending on how you buy Claude Code.

Your setupPer-user reporting
Claude for TeamsSpend report CSV in org analytics, updated daily
Claude for EnterpriseEnterprise Analytics API, with a read:analytics key
Claude Console (API)Console dashboard plus the Claude Code Analytics API, with an Admin API key
Bedrock, Google Cloud, or Microsoft FoundryOpenTelemetry export, or a gateway that tracks spend per key

OpenTelemetry is the only option that works on every setup and streams per-user token and cost metrics into your own stack in near real time.

Turning on the OTel metrics export
export CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_METRICS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
export OTEL_METRIC_EXPORT_INTERVAL=60000   # ms, this is the default

The metrics it emits are claude_code.session.count, claude_code.token.usage, claude_code.cost.usage, claude_code.lines_of_code.count, claude_code.commit.count, claude_code.pull_request.count, claude_code.code_edit_tool.decision, and claude_code.active_time.total. Cost is in USD; tokens are counted per type.

What the number means on a subscription

If you are on Pro, Max, or a Team seat, you are not billed per token, so a dollar figure derived from API rates is not an invoice. It is still the most useful number you can compute, for three reasons.

  1. It tells you whether your plan is good value. If a $100 Max 5x plan does $600 of notional API work a month, that is a clear answer. Anthropic's own published average is around $13 per developer per active day on metered billing, so a full-time month lands around $150 to $250.
  2. It attributes consumption. "This one repo is 70% of my usage" is actionable in a way that "I hit my limit again" is not.
  3. It prices habits. Seeing that running full test suites through the agent costs more than the rest of the session combined changes the habit within a day.

Common mistakes

MistakeEffectFix
Summing all four token counts at one rateWildly overstated costPrice each bucket at its own multiplier
Skipping deduplicationSlow upward drift nobody noticesUnique on message ID plus request ID
Using today's rates for old sessionsWhole history shifts after a repricingPrice each event at its own timestamp
Bucketing by UTC dayTotals disagree with your calendar near midnightBucket by local calendar day, as ccusage does
Ignoring the newest fileMissing the session you are in right nowAlways re-read the most recently modified file
Reading only ~/.claudeA second account's spend vanishesAlso read every CLAUDE_CONFIG_DIR you use
Trusting /usage as a billOff by any discount you negotiatedUse the Console usage page for authoritative billing

Questions people ask

In ~/.claude/projects/, one directory per project with JSONL files per session. Codex uses ~/.codex/sessions/. Both are plain local files you can read. A second account under CLAUDE_CONFIG_DIR keeps its own projects directory under that root.

Yes, but with caveats. /usage shows a session cost computed locally at standard list rates, so it ignores promotional pricing and contracted discounts. On a subscription it is a measurement, not a bill. cost.total_cost_usd in the status line JSON is the same figure.

An open-source CLI by ryoppippi that parses local agent session history and reports token usage and cost by day, week, month, session, or 5-hour billing window. It covers Claude Code, Codex, opencode, Gemini, Copilot and more, and is the de facto reference implementation.

Cache reads. In a long agent session they dominate the raw count and bill at 0.1x the base input rate. A session showing a million tokens of traffic can still cost under a dollar.

It is notional: what the same work would have cost on the API. That is exactly the number that tells you whether your subscription is good value and which project is consuming it.

Yes. Codex writes equivalent rollout files to ~/.codex/sessions/ with token counts, so the same approach works, and ccusage codex daily does it for you. The record shapes differ, so a homemade parser needs a branch per provider.

On Teams, export the spend report CSV from org analytics. On Enterprise, use the Enterprise Analytics API. On the Console, use the Claude Code Analytics API with an Admin key. On Bedrock, Google Cloud, or Foundry, export OpenTelemetry metrics, which is the only route that works everywhere.

1.25x the base input rate for a 5-minute cache write and 2x for a 1-hour write. The JSONL does not record which duration was used, so most tools assume 5 minutes, which is right for default interactive sessions.

Sources

Every figure above was read from these pages on August 2026. Vendors reprice without notice; if you find a stale number, tell us.

  1. ccusage reference implementation for parsing session history
  2. Claude platform pricing token rates and cache multipliers
  3. Claude Code: manage costs effectively /usage, per-user reporting routes
  4. Claude Code: monitoring usage OpenTelemetry metric names and env vars
Try it

One ledger across
every agent you run.

Continuum parses Claude Code and Codex history locally and shows dollars by repo, provider, model, and day, across multiple accounts. Free app, nothing leaves your machine.

free app · your subscriptions · local-first