meterly
v0.1.0
Published
See where your Claude Code spend actually goes — per project, model, MCP server, subagent and branch. Local-first, no API key.
Downloads
17
Maintainers
Readme
meterly
See where your Claude Code spend actually goes — per project, model, MCP server, subagent, skill and git branch.
Claude Code already writes every token count to disk. meterly reads those local transcripts and turns them into a cost report. No API key, no account, no network calls.
npx meterlyThere's also a VS Code extension — live cost in the status bar for the folder you're in, plus a full report panel. Install the .vsix from the latest release:
code --install-extension meterly-vscode-0.1.0.vsixExample output (illustrative figures):
═══ Claude Code spend ═══
Billable messages 4,120
Total tokens 980,455,000
Total cost $1184.00
Cost breakdown
input $2.10
output $141.60
cache read $712.40 1.0B tok
cache write $327.90 5m 40.0k / 1h 22.0M tok
By project
~/work/api-gateway $602.10 50.9% 498.0M tok
~/work/web-client $284.55 24.0% 221.0M tok
~/work/infra $167.30 14.1% 132.0M tok
By MCP server
postgres $188.40 15.9% 171.0M tok
playwright $141.20 11.9% 118.0M tok
github $96.75 8.2% 88.0M tokBudgets that warn before you overrun
A dashboard tells you what you already spent — the cheap half of the problem. By the time a number looks bad, the money is gone.
Drop a .meterly.json beside your project (or in your home directory):
{
"budgets": [
{ "name": "daily cap", "period": "day", "limit": 25 },
{ "name": "team", "period": "month", "limit": 2000, "warnAt": 0.7 },
{ "project": "backend", "period": "week", "limit": 100 }
]
}Budgets trip on trajectory, not just arrival. Spend is extrapolated over the remaining period, so $30 by 6am against a $100/day cap is flagged as heading for $120 while there's still time to act:
Budgets
[ over?] ███████|················· daily cap: on track to overrun — $30.00 of $25.00
with 75% of the day left, heading for $120.00
(| marks how far through the period you are)--check exits 3 when any budget is over or projected over, so it works as a guardrail rather than a report — wire it into CI, a cron job, or a shell hook:
meterly --check || notify-send "AI spend over budget"Exit codes are distinct on purpose: 3 is a budget breach, 1 is no data, 2 is bad usage — so a caller can tell a breach apart from the tool failing to run.
Why another usage tracker
Most of them get the arithmetic wrong, and none of them tell you which part of your setup is expensive.
1. One API response is many log records
Claude Code writes one record per content block — a thinking block, a text block, each tool_use — and every one of them repeats the same message-level usage object verbatim.
Sum the records and you count the same tokens three or four times. Across the transcript set this was developed against, assistant records outnumbered actual billable messages by 2.64× — so a record-summing tracker reports a bill more than two and a half times the real one.
meterly dedupes on message.id, which is the actual billable unit. (Not requestId — a server-side fallback re-runs one request on a second model, and both messages are billed.)
2. Cache writes are not one price
A 1-hour-TTL cache write costs 2× base input. A 5-minute write costs 1.25×. The logs break this out under cache_creation.ephemeral_1h_input_tokens / ephemeral_5m_input_tokens, but the flat cache_creation_input_tokens field lumps them together.
On the same dataset over 99% of cache-write tokens were 1-hour. Pricing them all at 1.25× understates the cache-write line by 37% — and cache writes were the second-largest cost component, so that is not a rounding error.
3. Things that aren't tokens still cost money
- Web search bills $10 per 1,000 searches, entirely separate from tokens. Invisible to anything that only sums token fields.
inference_geo: "us"applies a 1.1× multiplier to every token category. It's in the logs; nothing else applies it.- Fast mode on Opus 5 / 4.8 bills at $10/$50 rather than $5/$25.
- Code execution bills by container-hour with 1,550 free hours/month — so it is counted but deliberately not priced, because a per-call figure would be invented.
4. Unknown models are never silently free
A model with no known rate contributes $0 and a loud warning, and the report says the total is a floor. A confident wrong number is worse than a stated gap.
What it attributes
| Axis | Question it answers |
|---|---|
| Project (cwd) | Which repo is burning the budget? |
| Model | What's the mix, and what does each tier actually cost? |
| MCP server | Which integration is expensive? |
| MCP tool | Which individual call inside it is expensive? |
| Skill / plugin | Which of these is pulling its weight? |
| Session | Which single conversation ran away with the budget? |
| Git branch | What did that refactor cost? |
| Day | Trend over time |
| Subagents (isSidechain) | How much goes to delegated work? |
| Failed API calls | Spend that returned nothing |
| Compaction | Cost of context summarisation |
Usage
npx meterly # everything
npx meterly --since 7d # last week (also 24h, 30m, or 2026-07-01)
npx meterly --project api-gateway # one project
npx meterly --top 25 # more rows
npx meterly --json # full report as JSON
npx meterly --csv > spend.csv # every dimension as CSV, for chargeback--root <path> or CLAUDE_CONFIG_DIR if your config isn't at ~/.claude.
As a library
Zero dependencies, streaming, and safe on large transcripts — real sessions reach 150 MB+, so nothing is ever fully loaded into memory.
import { findTranscripts, parseTranscript, buildReport } from 'meterly';
const seen = new Set<string>(); // shared: resumed sessions repeat messages
const records = [];
for (const f of await findTranscripts()) {
const { records: r } = await parseTranscript(f.path, seen);
records.push(...r);
}
const report = buildReport(records);
console.log(report.totals.cost.total);
console.log(report.byMcpServer);
console.log(report.diagnostics.unpricedModels); // always check thisPricing is overridable if rates change before this package does:
buildReport(records, {
models: { 'claude-opus-5': { input: 4, output: 20 } },
serverTools: { web_search_requests: 0.008 },
});Accuracy
Rates are transcribed from Anthropic's published pricing (verified 2026-07-27) and live in one table in src/pricing.ts. Cache multipliers (1.25× / 2× / 0.1×), the batch discount (50%), the US inference-geo premium (1.1×) and fast-mode rates are applied as documented multipliers rather than hardcoded per-model figures, so they stay correct as models are added.
These are estimates for attribution, not an invoice. Your bill is what Anthropic's console says. Discounts, enterprise rates and Bedrock/Vertex/Foundry pricing are not modelled.
Privacy
Everything runs locally. meterly reads token counts, model IDs, timestamps, working directories and branch names — it never reads message content, and it never opens a network connection. Grep the source: there is no HTTP client in it.
License
MIT
