Skip to content
Engineering · Article

I Built a Cost Dashboard for Claude Code. Then I Put It on a Raspberry Pi.

Claude Code saves every session to local JSONL files. I wrote a tool that reads them, works out what each session cost, and serves it over a REST API. Then I put it on my always-on Pi and hooked it up to Telegram.

Ali Rezaiyan6 min read

I run Claude Code all day on a Raspberry Pi 5. It answers Telegram messages, runs scripts, and manages services. It's really useful, and it's also spending money the whole time. For a while I had no quick way to see how much.

I could open Anthropic's usage dashboard in a browser, but I didn't want to. I wanted to type /cost in Telegram and see today's spend in under a second, wherever I was.

So I built Ledger.

The data is already on disk

Claude Code saves every conversation as a JSONL file in ~/.claude/projects/. Each line is an event: a user message, an assistant reply, a tool call. The assistant lines include token usage: input, output, cache writes, cache reads, and even the number of web searches.

That's all you need to work out exactly what a conversation cost. You don't need an API key or any outside service, because the data is already on your machine.

The pricing has more parts than you'd expect:

  • Input tokens are the base rate, for what you send to the model.
  • Output tokens usually cost 5x the input rate.
  • Cache writes with a 1-hour TTL cost 3.75x the input rate.
  • Cache writes with a 5-minute TTL cost 1.25x.
  • Cache reads cost 0.10x.

If you get the cache part wrong, your estimate can be off by 20 to 40 percent. Ledger uses the detailed breakdown when it's there, and falls back to the combined cache_creation_input_tokens field for older files.

What Ledger does

Ledger reads every JSONL file in your Claude projects folder, goes through each assistant entry, and adds up cost, tokens, cache hit rate, and timing. It all happens locally, with no network calls.

You can use it three ways:

  • As a CLI: ledger prints a cost summary and exits, and ledger today shows just today.
  • As a terminal dashboard: ledger tui opens an interactive view built with Ink.
  • As a web dashboard: ledger open starts an Express server and opens a React + Recharts page at localhost:4200.

The REST API behind the web dashboard is what makes the Pi setup work. Endpoints like /api/summary, /api/conversations, and /api/status return JSON that anything can read.

Slash commands for tracking sessions

Ledger also comes with slash commands you can drop into your project's commands/ folder. They let Claude Code keep track of its own work sessions:

/project:session-start feature "Add user auth"
# ... work happens ...
/project:session-update "OAuth flow working, writing tests"
# ... more work ...
/project:session-end

Each session turns into a Markdown file with YAML frontmatter: start and end time, cost, commits, lines changed, and the model used. Ledger reads these together with the JSONL data and works out how efficient each session was.

Two numbers worth watching

Apart from raw cost, two numbers turned out to be the most useful.

Cache hit rate

Cache hit rate is cache reads divided by all cache activity (reads plus writes). A high rate means Claude Code is reusing cached context instead of sending it again every turn.

For an always-on bot with a stable system prompt, you want this above 70%. It drops when a session starts cold after a restart, and climbs back once things have been running for a while. Watching it over time shows whether you're actually getting the benefit of prompt caching or just paying to write the cache.

Dead-end sessions

A dead-end session is one that cost something real (more than $0.10) but ended with no commits. Money spent, nothing to merge.

Sometimes that's fine: exploring, researching, debugging. But if a session was supposed to produce code and didn't, I want to know.

Ledger flags these on its own. If you track the dead-end rate by session type (feature, bug, refactor, explore), you start to see where AI-assisted work pays off and where it doesn't.

Running it on the Pi

My Pi 5 already runs Claude Code in a tmux session managed by systemd, so adding Ledger as a second service was easy.

Install Node.js 22 LTS, then:

npm install -g @rezaiyan/ledger

The only difference between the Mac and the Pi is where the JSONL files live: /Users/ali/.claude/projects on the Mac and /home/ali/.claude/projects on the Pi. Ledger takes a --dir flag, so I just pass the right path in the service file:

[Unit]
Description=Ledger — Claude Code cost and session API
After=network.target

[Service]
Type=simple
User=ali
ExecStart=/usr/local/bin/ledger serve \
  --dir /home/ali/.claude/projects \
  --sessions-dir /home/ali/sessions \
  --port 4200
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

When idle, the server uses about 30 MB of RAM. Reading a big conversation history at startup takes a couple of seconds, and after that results are cached for 30 seconds, so API calls come back right away.

Updating takes one line:

ssh ali@pi5.local "npm install -g @rezaiyan/ledger && sudo systemctl restart ledger"

Hooking it up to Telegram

The Pi already runs a Python admin bot that controls Claude Code: it restarts it, checks its health, and manages a command queue. Adding Ledger meant a small HTTP helper and five new commands.

LEDGER_URL = "http://localhost:4200"

def ledger_api(path):
    try:
        req = urllib.request.Request(f"{LEDGER_URL}/api/{path}")
        with urllib.request.urlopen(req, timeout=5) as r:
            return json.loads(r.read()), None
    except urllib.error.URLError:
        return None, "Ledger service is not running."

The commands:

  • /overview: today's cost, the active session, and this month's total
  • /cost: today, this month, and all-time spend in three lines
  • /sessions [N]: the last N conversations with cost, cache rate, duration, and model
  • /lastsession: everything about the most recent conversation
  • /efficiency: cache hit rate, average cost per conversation, and dead-end count

They all answer in under a second, and nothing leaves the Pi.

What changed

Seeing costs as they happen changed how I run the Pi in three ways.

First, which model to use. I run Haiku on the Pi because it's 5 to 10 times cheaper than Sonnet for the chat and automation work the bot does. Before Ledger that was a guess. Now I can see the cost of each conversation, and it confirmed the split: Sonnet on the Mac for hands-on development, Haiku on the Pi for the always-on stuff.

Second, how I set up sessions. I started tracking which kinds of tasks end up as dead ends. Open-ended research was the worst, not because it failed, but because I wasn't starting those sessions with a clear idea of what I wanted out of them. Now I do.

Third, when to restart. The cache hit rate shows what a restart costs. When the Pi has been running for a while, the rate sits above 80%. After a restart it drops close to zero and takes a while to recover. So every unnecessary restart now has a price attached, instead of a vague "the cache will be cold" feeling.

Try it

npm install -g @rezaiyan/ledger

ledger          # cost report
ledger today    # today's summary
ledger tui      # interactive dashboard
ledger open     # web dashboard at localhost:4200

The source is at github.com/rezaiyan/ledger. MIT licensed.

If you use Claude Code a lot, on a Pi, a VPS, or just your laptop, it's worth knowing what each session costs. The data is already on your disk. You just have to read it.

Enjoyed this?

There's more where that came from.

All posts