# My Subagents Were Eating 48% of My Tokens. So I Gave Them Hard Budget Caps — Enforced by Hooks, Not Dashboards

> Source: <https://dev.to/haoli/my-subagents-were-eating-48-of-my-tokens-so-i-gave-them-hard-budget-caps-enforced-by-hooks-not-9e0>
> Published: 2026-10-11 09:29:02+00:00

A few weeks ago a measurement made the rounds: developer aidiveyt logged a

month of Claude Code usage — 455 sessions, 2,631 subagent runs — and found

subagents had consumed **48.1% of all tokens**. The median subagent's *first request* alone was 47,117 tokens. Nobody approved that spend. It just

`Task` call at a time.
Simon Willison's reaction was the whole community's reaction: *"hard budget caps, please, now."*

I had the same problem and the same reaction. So I built

`subagent-budget`: per-agent-type token **and** dollar budgets that are

**enforced by Claude Code hooks** — an over-budget subagent is refused at

launch, with the reason shown to the agent. A dashboard that can't stop a

launch is a suggestion. This is a cap.

```
pip install subagent-budget

subagent-budget init --default-tokens 1000000 --default-usd 50
subagent-budget set-budget --pattern "Explore*" --tokens 200000 --usd 10
subagent-budget sync        # backfill from ~/.claude/projects transcripts
subagent-budget report
```

Then the enforcement part — one block in `~/.claude/settings.json`:

```
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Task",
        "hooks": [{ "type": "command", "command": "subagent-budget hook --event pre" }]
      }
    ]
  }
}
```

Now every subagent launch goes through a budget check first. Under budget:

it launches, and the hook prints the remaining headroom. Over budget: exit

code 2 (Claude Code's blocking-hook signal), and Claude sees exactly why:

```
subagent-budget blocked launch of 'Explore auth code' (Explore):
token budget exceeded: used 210,441 / 200,000 tokens
```

That last part is the whole point. Prompt-level instructions ("please don't

spawn too many subagents") don't survive contact with an agent mid-task. A

hook that returns a non-zero exit sits *below the prompt layer* — the model

can't argue with it, negotiate with it, or forget it.

There's already a tool in this space — `subagent-ledger` — and it's honest

about what it is: a *display*. It shows token counts, clears at session end,

and can't stop anything. `subagent-budget` is built on the opposite premise:

`~/.config/subagent-budget/ledger.jsonl` and survives session ends. A
budget that resets every session isn't a budget.
Budget rules are globs matched against both the agent's description and its

subagent type, first match wins — so `Explore*` can have a tight shared pool

while everything else falls back to the default. Either dimension can be

left unlimited.

If you already track spend across `claude --resume` with

[resume-budget-guard](https://github.com/hahahahahahahahah6/resume-budget-guard),

the ledger formats are deliberately compatible (`ts`, `kind`, `cost_usd`

records). One command folds resume spend into the same caps:

```
subagent-budget import-rbg
# imports ~/.resume-budget-guard/ledger.jsonl as agent "rbg:<session-key>"
```

Imported spend counts toward the matching budget rules, so a resume can no

longer silently reset what a subagent cap was guarding. Idempotent — re-run

it anytime.

`sync` records one ledger entry per Task
call using a configurable estimate (default 47,117 — the measured median).
Use `record --tokens` with exact figures from `--output-format json` when
it matters.`model_rates` or pass `--cost` for exact numbers.`record`.
Stdlib only, zero dependencies, MIT. `pip install subagent-budget` —

[GitHub](https://github.com/hahahahahahahahah6/subagent-budget) ·

[PyPI](https://pypi.org/project/subagent-budget/).
