How to Run a 20-Agent Company Inside One Claude Code Session
20 sub-agents working under one session is a documented ceiling you set on purpose, which is a company's worth of parallel work run by one person. Left on default with a frontier seat underneath, the same fan-out ended somebody's five-hour window in 10 minutes and nothing about it looked like a malfunction.
>This caps the blast radius. Claude Fable 5 builds the rest of the split-stack around it: the audit, the plan, the handoff contract, and the ledger that proves which seat should have done the work.

Hello builders,
20 sub-agents working in parallel under one session is a company’s worth of output run by one person, and it is a number you set on purpose rather than one you discover. Discovered instead, it looks like the account of a session that decided to spawn 75, yes seventy-five sub-agents to complete a task, and zipped through a five-hour limit in literally ten minutes at $48.75, with nothing malfunctioning. A claude code subagent spawn limit is two environment variables and one line of frontmatter, and we are going to price the runaway exactly and then make it impossible.

Four stages, three warning rows, and one line of frontmatter, and the three rows in the middle are what an uncapped fan-out actually bills.
What a runaway actually is
The mechanic is not what most people picture, and it decides which lever actually works.
A single orchestrator, the session you are driving, can fan out to many sub-agents in one turn. It decides a job would go faster with help and launches ten, or thirty, or seventy-five of them at once. What makes that hurt isn’t the count, it’s what each one is running: if your session is on Fable 5, every branch is a full Fable instance reading context and thinking at $10 in and $50 out per million tokens.
What flips the switch is ultracode, and the half of that feature people miss is the standing permission. It is the license to fan out without asking, handed over quietly through mid-conversation system messages. Once it is granted, the orchestrator can decide mid-task that reviewing its own work warrants thirty-seven agents, and just do it. The person who watched 37 agents eat a five-hour limit in 27 minutes was not hit by a bug. They were hit by a permission they didn’t realise they’d handed over.
Two documented ceilings bound this, and both are configurable:
- 20 concurrent sub-agents per session, set by
CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS(any positive whole number; sessions with ultracode active are exempt). - Three layers of nesting below the main conversation, set by
CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH. Set it to1to turn nesting off entirely. (Subagents, Claude Code docs)
That second one deserves a flag, because the received wisdom is now wrong. It used to be true that a sub-agent could not spawn its own, so a fan-out could not compound. The docs are explicit that this changed: for two releases the depth limit defaulted to one, and then the default went to three. So if we learned “nesting is blocked” at any point, that was true of two releases and is not true of the one we are running now, and the depth we get is whatever the default happens to be.
{
"env": {
"CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH": "1",
"CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS": "4"
}
}
That goes in your settings, and setting it takes about thirty seconds.
Now price it, because “burned my limit” is a feeling and a dollar figure is something you can act on. Say each fanned-out agent does a modest amount of work: reads about 40,000 tokens of context to orient itself, writes about 5,000 tokens of review.
RATES = {"fable-5": (10, 50), "opus-4-8": (5, 25),
"sonnet-5": (2, 10), "haiku-4-5": (1, 5)}
def cost(model, tin, tout):
rin, rout = RATES[model]
return tin / 1_000_000 * rin + tout / 1_000_000 * rout
per_agent = cost("fable-5", 40_000, 5_000)
print(f"per agent : ${per_agent:.2f}")
print(f"37 agents : ${per_agent * 37:.2f}")
print(f"75 agents : ${per_agent * 75:.2f}")
per agent : $0.65
37 agents : $24.05
75 agents : $48.75
Sixty-five cents a branch. Forty-eight seventy-five for the full seventy-five, in ten minutes, for a review nobody requested. And that sticker is the optimistic reading, because Fable is the most rate-limited seat on the stack. Here is what the published per-minute ceilings actually are:
- Claude Fable 5: Start: 500,000 input tokens per minute, 100,000 output. Build: 1,500,000 / 300,000. Scale: 4,000,000 / 800,000.
- Claude Sonnet 5: Start: 2,000,000 / 400,000. Build: 5,000,000 / 1,000,000. Scale: 10,000,000 / 2,000,000.
- Claude Opus 4.x (one combined bucket across 4.8, 4.7, 4.6 and 4.5): Start: 2,000,000 / 400,000. Build: 5,000,000 / 1,000,000. Scale: 10,000,000 / 2,000,000.
Only uncached input counts toward the input ceiling; cache reads do not. (Rate limits, Claude Platform Docs)
Fable gets a quarter of Sonnet’s throughput at every tier. Divide the burst against it:
burst_in = 75 * 40_000
burst_out = 75 * 5_000
TIERS = {"start": (500_000, 100_000), "build": (1_500_000, 300_000),
"scale": (4_000_000, 800_000)}
for tier, (itpm, otpm) in TIERS.items():
print(f"{tier:5} input {burst_in/itpm:.1f}x ceiling | output {burst_out/otpm:.1f}x ceiling")
start input 6.0x ceiling | output 3.8x ceiling
build input 2.0x ceiling | output 1.2x ceiling
scale input 0.8x ceiling | output 0.5x ceiling
So on the entry tier that burst wants six times the input the ceiling allows. A chunk of those agents get throttled, retry, and burn tokens on work that crashes and reruns. The real bill is the forty-eight dollars of successful work plus the crashed retries on top. We paid frontier rates for the least valuable work on the stack, and the throttle charged us extra for the privilege.
The one line that defuses it
The fix is not fearing sub-agents. It is making sure that when they fan out, they land on a seat we have already made cheap. A sub-agent’s frontmatter can pin its own model:
---
name: executor
description: Types specified changes from a handoff. No judgment, ever.
model: sonnet
---
model: sonnet on your executor agents means a fan-out, if it happens, happens on a seat you can afford. Run the same seventy-five-agent burst with Sonnet underneath and the arithmetic falls out of the same rate table: $0.13 a branch instead of $0.65, so $9.75 instead of $48.75, and the burst now sits inside Sonnet’s much higher ceilings instead of six times over Fable’s.
Keep the orchestrator on the expensive seat if the judgment genuinely needs it. We just never let the branches inherit it by default.
That default is worth checking by hand, too. An engineer on the Claude Code team, demoing the feature that generates multi-agent workflow plans, opened the generated file on camera and found every sub-agent had defaulted to Fable 5, and said out loud that not every one of those seats needed a model that strong. When the people who built the feature question its defaults on sight, take the hint: any tool that fans out on your behalf gets its model fields read before it runs.
The four stages
The picture at the top is a pipeline, and each box is a thing we actually do.
Scope. Decide which plan steps run, and name the only files that may change. This is the step that prevents the quiet runaway nobody screenshots: an executor handed a two-file task that decides to understand the whole repository first, pulls a hundred thousand tokens of context it never needed, and only then makes the two-line change. You’ve paid input rates on all of it. A files-touched list shuts the crawl down before it starts.
Delegate. Hand it over with the limits in the same breath. That means a concurrency cap, an effort cap, the model, and a written rule about when fanning out is even allowed:
## Sub-agent budget (hard rules)
- Never run more than 4 sub-agents at once. Prefer serial execution.
- Only fan out for genuinely independent work with disjoint files.
- Every sub-agent runs --model sonnet unless a step is tagged for judgment.
- Make the smallest change that passes the gate. Do not refactor,
do not add abstractions, do not improve unrelated code.
That last rule catches the third runaway, the one that costs the most over a month because it happens constantly: gold-plating. Ask for a rounding fix and get back a rounding fix plus a refactor of the billing module, a new abstraction layer, and three “while I was in there” improvements. Every extra token cost money, and every one is unreviewed scope creep somebody now has to read and probably revert.
Checkpoint. Have the run write its progress to a file as it goes: steps done, gate results, what is left, spend so far. When the conversation compacts or you hand the rest to a fresh executor, the file is the memory that survives. A run that keeps its state only in the chat forgets what it did the moment the window fills.
Escalate or accept. When the executor hits a step that stopped being typing and became a decision, stop it and send that one step up on purpose, logged. Then send it back down. The cheap seat runs by default, and the expensive one gets invited back for that one decision and then sent away again.
Run the webhook fix that way and the numbers aren’t close. Sonnet types the two specified steps, Fable makes one policy call, and the total is $0.245 against $48.75 for the fanned-out version of identical output. That is 199 times cheaper. At today’s published Sonnet rate the same run comes in at $0.215 and the gap widens to 227 times, which is a nice problem to have.
Three controls, one kill-switch
These get conflated constantly, and the differences decide whether a runaway actually halts.
max_tokens caps one response’s length. It is a hard ceiling and the model is not aware of it.
task_budget gives the model a token budget it can see, with a running countdown, so it self-moderates and wraps up gracefully. It is a suggestion the model tries to honour rather than a wall, so it can overshoot, and the minimum is 20,000 tokens.
--max-budget-usd is the only one that genuinely stops a run. In print mode, claude -p --max-budget-usd 5.00 halts when API spend crosses five dollars. Spend from sub-agents counts toward the cap, spawning another once the cap is reached fails with Budget limit reached, and Claude Code stops background sub-agents still running. (CLI reference, Claude Code docs)
claude -p --max-budget-usd 5.00 --output-format json "$(cat delegation-brief.md)" > run.json
The catch is that it works with print mode only, so it caps scripted work and not an interactive session, which covers the unattended runs where nobody is watching the meter anyway.
So pick the smallest of these that fits what you are doing, then go and do the boring part first. Open your settings, set the depth to 1 and the concurrency to 4, open every agent definition you own, and read what is in its model: field. Whatever we find in those fields is the per-branch rate we will pay the next time something fans out, which on the numbers above is either thirteen cents or sixty-five.
Now go build something this weekend!
John Cook