# How to set a hard daily spend cap on Claude Code

> 5-minute setup. Anthropic does not ship a hard $/day cap on raw API billing. This tutorial walks the install, configuration, and SIGTERM-on-breach behavior of BudgetClaw end to end. Per-project and per-branch attribution. Phone push via ntfy. Zero keys, zero prompts, zero latency added.

- HTML version: https://roninforge.org/tutorials/how-to-set-a-hard-spend-cap-on-claude-code
- Tool used: BudgetClaw - https://roninforge.org/budgetclaw.md
- Audience: developers using Claude Code on raw API billing (Pro/Max users get token tracking but no dollar cost)
- Last updated: 2026-04-28 against BudgetClaw v0.1.5

## Why this matters

After 2026-04, solo Claude Code users on raw API billing have no first-party way to cap spend per project or per git branch. Native `/cost` reports the bill after the fact. One stuck agent loop on a feature branch can burn $500 before you notice. This tutorial sets a hard daily cap that SIGTERMs the Claude Code process when breached, so the bill stops where you said it stops.

## Step 1 - Install

    curl -fsSL https://roninforge.org/get | sh

Or via Homebrew (macOS, Linux):

    brew install roninforge/tap/budgetclaw

Verify:

    budgetclaw version

## Step 2 - Initialize

    budgetclaw init

This creates the config directory at `$XDG_CONFIG_HOME/budgetclaw/` (default `~/.config/budgetclaw/`) and the SQLite state DB at `$XDG_STATE_HOME/budgetclaw/state.db` (default `~/.local/state/budgetclaw/state.db`). It prints both paths so you know exactly where state lives.

The config file is **TOML**, not YAML, in case you want to edit it directly.

## Step 3 - Set a daily cap

    budgetclaw limit set --project myapp --period daily --cap 5.00 --action kill

This caps the project named `myapp` (BudgetClaw infers project name from the working directory and `.git/HEAD`) at $5/day. When the cap is breached, BudgetClaw sends SIGTERM to the matching `claude` process and writes a lockfile to prevent silent relaunch.

You can layer caps. For a feature branch you expect to be expensive, narrow the rule:

    budgetclaw limit set --project myapp --branch "feature/expensive" --period daily --cap 1.00 --action warn

`--action warn` does not kill the process - it only sends the phone push. Useful when you want awareness without enforcement.

## Step 4 - Configure phone push via ntfy

    # 1. Install the ntfy app on your phone (iOS or Android).
    #    https://ntfy.sh/docs/subscribe/phone/

    # 2. Generate a secret topic name (so nobody else can guess it).
    TOPIC="budgetclaw-$(openssl rand -hex 12)"
    echo "Your topic: $TOPIC"

    # 3. Subscribe to that topic in the ntfy app.

    # 4. Tell BudgetClaw to push to it.
    budgetclaw alerts setup --topic "$TOPIC"

ntfy.sh is the public free instance. You can also self-host ntfy.

## Step 5 - Start the watcher

    budgetclaw watch

The watcher tails `~/.claude/projects/*/*.jsonl` using inotify on Linux and FSEvents on macOS. Each new entry is parsed for token counts, priced, and attributed to `{project, branch}`.

Run it in a separate terminal, or under a process supervisor. It uses ~5 MB of RAM and is mostly idle.

## Step 6 - Verify the breach path (test it before you trust it)

The whole point is that the SIGTERM fires when you blow past the cap. Test it deliberately the first time you set up a cap, while you have the watcher in front of you:

    # In a scratch project, set a tiny cap.
    cd ~/scratch
    budgetclaw limit set --project scratch --period daily --cap 0.05 --action kill

    # Open Claude Code and have it do something cheap-ish.
    claude

    # Within seconds of crossing $0.05 you should see:
    #   - the watcher log a "BREACH" line for the project
    #   - the claude process get SIGTERM'd (window closes / the CLI exits)
    #   - your phone get the ntfy push

If any of those three did not happen, run `budgetclaw status` to see whether the events were even attributed to the right project. Common causes: the watcher was not running, or `cwd` did not resolve to a `.git`-rooted project.

## Step 7 - After a kill

The lockfile prevents silent relaunch. To resume:

    budgetclaw unlock

Then re-open Claude Code as normal. Adjust the cap if you decided your initial estimate was wrong.

## Step 8 - Sanity-check spend over time

    budgetclaw status

Shows today's spend by project and branch. To see what's been priced in detail:

    budgetclaw pricing list      # known models with rates
    budgetclaw pricing diagnose  # any unknown model IDs in your JSONL?

If `pricing diagnose` returns a model you have not seen before, file an issue on the BudgetClaw repo - the daily pricing audit GitHub Action should already have surfaced it within 24 hours, but a user-reported model gets it triaged faster.

## Troubleshooting

**The watcher reports zero spend.** Check `budgetclaw status` and `budgetclaw pricing diagnose`. The most common cause is that the JSONL log lines use a model ID that is not in the embedded pricing table. Then file an issue.

**The kill does not happen.** Check that `budgetclaw watch` is actually running. Check that the rule applies to the right project: run `budgetclaw limit list` and confirm the project/branch fields match the project you are working in.

**The phone push does not arrive.** Check that the ntfy app is subscribed to the same topic you configured. Run `budgetclaw alerts setup --topic <topic> --test` to fire a test push.

**The kill happens but Claude Code relaunches.** Run `budgetclaw unlock` to clear the lockfile. If it relaunches without unlock, file an issue with the watcher logs - this should not happen.

## Trust pledge

BudgetClaw is not a proxy. It does not sit between Claude Code and the Anthropic API. It does not see your prompts, your responses, or your API key. It only reads the `usage` and `cwd` fields of the JSONL files Claude Code already writes to disk.

Zero keys, zero prompts, zero latency added.

## Source

- BudgetClaw: https://github.com/RoninForge/budgetclaw
- This tutorial: https://roninforge.org/tutorials/how-to-set-a-hard-spend-cap-on-claude-code
