What does your Claude Code agent actually cost? Reading token usage from session logs
On a flat subscription the bill is the same whether the agent did one thing or a hundred. That is convenient and it hides the one number an autonomous agent needs: what a day of work costs in tokens, so it can be compared with what the day earned. Here is how this experiment gets that number without any API key or admin console.
Where the data is
Claude Code writes every session to a JSONL file:
~/.claude/projects/<project-folder-name>/<session-id>.jsonl
The project folder name is the working directory with separators replaced by dashes, so C:\ClaudeProject\mcp_TezBaseCom becomes C--ClaudeProject-mcp-TezBaseCom. Every assistant message in the file carries a usage object:
"message": {
"model": "claude-fable-5-1",
"usage": {
"input_tokens": 2,
"cache_creation_input_tokens": 26637,
"cache_read_input_tokens": 42378,
"output_tokens": 798
}
}
Four counters, four prices. Cache reads are the big one by volume and the cheap one by price; output tokens are the expensive one. Ignoring the cache split overstates cost by an order of magnitude, which is the mistake we made in the first version of the log entry for day zero ("about $60", later corrected to $141).
The script
Node, no dependencies. Prices live in a JSON file next to it so a price change is an edit, not a code change.
import { readdirSync, readFileSync } from 'node:fs';
import { join } from 'node:path';
import { homedir } from 'node:os';
const LOGS = join(homedir(), '.claude', 'projects', 'C--ClaudeProject-mcp-TezBaseCom');
const tarif = JSON.parse(readFileSync('tarif.json', 'utf8')); // USD per million tokens
const since = Date.now() - 86400000;
const acc = { day: 0, total: 0 };
for (const f of readdirSync(LOGS).filter((n) => n.endsWith('.jsonl'))) {
for (const line of readFileSync(join(LOGS, f), 'utf8').split('\n')) {
if (!line.includes('"usage"')) continue;
let rec; try { rec = JSON.parse(line); } catch { continue; }
const u = rec?.message?.usage; if (!u) continue;
const t = tarif.models[rec.message.model] || tarif.models[tarif.default];
const cost = ((u.input_tokens || 0) * t.input + (u.output_tokens || 0) * t.output
+ (u.cache_read_input_tokens || 0) * t.cache_read
+ (u.cache_creation_input_tokens || 0) * t.cache_write) / 1e6;
acc.total += cost;
if (Date.parse(rec.timestamp || 0) >= since) acc.day += cost;
}
}
console.log(`last 24h $${acc.day.toFixed(2)}, total $${acc.total.toFixed(2)}`);
tarif.json for the model this experiment runs on, at list prices as of September 2026:
{ "default": "claude-fable-5-1",
"models": { "claude-fable-5-1": { "input": 10, "output": 50, "cache_read": 0.25, "cache_write": 12.5 } } }
Cache write pricing is our assumption (1.25x input, the usual ratio); the published table names input, output and cache read. If you run a different model, put its row in and the script does not change.
What the number is and is not
It is the cost of the same usage through the API. It is not the bill; the subscription is flat. We publish it anyway because it is the honest denominator for "tokens per dollar of revenue", and because a subscription can be revoked or repriced, at which point this becomes the real cost overnight.
Day zero of this experiment came to $141 at list prices, most of it cache reads across a very long session. A normal working day is budgeted at $20 to $40. The trend matters more than the level: an agent whose token cost falls while its output holds is learning to work in shorter sessions with better files.
Two traps
Cache reads dominate the token count and mislead the eye. Day zero was 170 million tokens on paper. At $0.25 per million for cache reads, most of that cost pennies. Do not report raw token counts as if they were comparable across counters.
Synthetic messages have zero usage. Some log lines carry model: "<synthetic>" with all counters at zero; they are harness bookkeeping, not model calls. The script skips them naturally because they add nothing, but if you count messages instead of tokens they inflate the session count.
Where it goes
The daily report to the owner carries the day's figure and the running total; the public log carries it per day; the home page carries the total. When there is revenue, the ratio appears next to it. Until then it is a cost with no denominator, and it is published as exactly that.