A self-contained tool to measure and visualize how you're spending tokens with Claude Code over the last N days. No telemetry, no API calls — it just reads the JSONL session transcripts that Claude Code already stores on your machine.
- Totals — billable tokens (input + output + cache_create), cache hit rate, sessions, messages, active days, daily peak.
- Daily timeseries — billable + cache_read on a dual-axis area chart.
- By model — donut split between Opus / Sonnet / Haiku (and any older versions you used).
- Interactive vs scheduled — separates real sessions from scheduled-task runs so you can see what's "running while you sleep" vs what you typed yourself.
- Composition — input vs output vs cache_create.
- By project — horizontal bars per project root (each cwd shows up as one slug).
- Tools most invoked — Bash, Read, Edit, Write, Agent, MCP tools…
- Calendar heatmap — one cell per day, intensity by billable.
- Top sessions — the 15 heaviest sessions in the window with model, duration, cache read, billable, and a preview of the first user prompt.
- Audit findings — automatic recommendations: cache hit too low, Opus share too high, marathon sessions, mirrored project paths bootstrapping the cache twice, scheduled-task share, etc.
Claude Code is incredibly efficient if you let the prompt cache do its job, and incredibly wasteful if you don't. After the first month on a Max plan you usually have no idea where the tokens went. This dashboard answers that in one screen, with concrete fixes per finding.
Requirements: Python 3.10+ and a modern browser. No pip dependencies.
git clone https://github.com/<user>/claude-code-token-audit.git
cd claude-code-token-audit
# 1. Generate the data file (reads ~/.claude/projects/*/*.jsonl)
python analyze_tokens.py --days 30 --out token_data.json
# 2. Serve the dashboard locally (any static server works)
python -m http.server 8125Then open http://localhost:8125/dashboard.html.
The dashboard fetches
./token_data.jsonso it must be served, not opened withfile://.
python analyze_tokens.py [--days N] [--projects-root PATH] [--out FILE] [--top-sessions N]
| flag | default | description |
|---|---|---|
--days |
30 |
Window size in days |
--projects-root |
~/.claude/projects |
Override if you keep Claude Code data elsewhere |
--out |
token_data.json |
Where to write the JSON the dashboard reads |
--top-sessions |
25 |
How many heaviest sessions to include in top_sessions |
The script writes a single JSON file with this shape:
You can pipe this into your own dashboard, Grafana, BI tool, etc.
Heuristic, conservative:
- If the session's recorded
cwdcontainsscheduled-tasks— it's scheduled. - Else if the first user prompt contains any of
SCHEDULED_PROMPT_HINTS(configurable inanalyze_tokens.py) — scheduled. - Otherwise — interactive.
If you don't use scheduled tasks, everything will land in interactive. That's fine.
- The script reads only your local
~/.claude/projects/files. - The dashboard runs entirely in the browser. The only network calls are to a CDN for ApexCharts and Google Fonts.
token_data.jsonincludes the first ~140 chars of the first user prompt of each session as preview, so you can recognise sessions. If you plan to share the JSON publicly, sanitize that field first.
If you want a monthly re-run, register it as a Claude Code scheduled task (cron 0 9 24 * * for "9 AM on day 24 of each month"). The analyze_tokens.py script is idempotent and safe to re-run any time.
MIT — see LICENSE.
{ "meta": { "generated_at", "window_days", "window_start", "window_end", "files_scanned", "total_lines", "messages_in_window", … }, "totals": { "input", "output", "cache_create", "cache_read", "billable_total", "cache_hit_rate", "sessions", "messages", "active_days" }, "daily_series": [ { "date", "input", "output", "cache_create", "cache_read", "billable_total", "messages" }, … ], "by_project": [ { "project", "label", "billable_total", "sessions", … } ], "by_model": [ { "model", "billable_total", "messages", … } ], "by_kind": [ { "kind": "interactive" | "scheduled", … } ], "by_tool": [ { "tool", "uses" } ], "top_sessions": [ { "session_id", "project", "kind", "first_ts", "last_ts", "duration_min", "messages", "total_billable", "cache_read", "models", "top_tools", "preview" } ] }