Skip to main content

Engineering

Why Heavy Sub-Agent Fan-Outs Hit Claude Rate Limits

Diagram of a parent Claude Code agent fanning out to multiple sub-agents and stacking against a single TPM ceiling

Sub-agent fan-outs feel like parallelism. To your Claude.ai account, they look like a stampede. Eight Task spawns from a single parent push nine simultaneous conversations through one TPM bucket — and the first 429 is usually the cheap one. Here's the concurrency math, the failure mode, and the structural fix.

What sub-agent fan-out actually means in Claude Code

Claude Code's Task tool lets a parent agent spawn sub-agents. Each sub-agent runs with its own system prompt, its own tool surface, and its own conversation transcript. From the parent's perspective, a Task call returns a single string. From Anthropic's perspective, it's a separate API conversation that consumes tokens against the same account that spawned it.

That second sentence is where developers get into trouble. The parent's mental model is "I delegated a task." The API's view is "this account now has nine simultaneous streaming completions, all charging tokens against the same per-minute bucket."

Sub-agents matter for real work. They isolate context, let you run specialist prompts, and parallelize reviews. The discipline articles tell you to spawn them when you have genuinely independent work to do. What the discipline articles do not tell you is what happens when six of them try to start at the same time on a single Pro account.

Here is a representative fan-out plan, the kind you write into a TodoWrite block and dispatch in one parent message:

// Parent agent dispatches six sub-agents in a single message.
// Each Task call is a separate API conversation.
[
  { "tool": "Task", "agent": "reviewer-security",   "prompt": "Audit src/Auth/ for injection..." },
  { "tool": "Task", "agent": "reviewer-perf",       "prompt": "Profile src/Reader/ for N+1..." },
  { "tool": "Task", "agent": "reviewer-tests",      "prompt": "Check test/ coverage for Auth..." },
  { "tool": "Task", "agent": "fixer-lint-php",      "prompt": "Run phpstan, fix violations..." },
  { "tool": "Task", "agent": "fixer-lint-js",       "prompt": "Run eslint, fix violations..." },
  { "tool": "Task", "agent": "docs-writer",         "prompt": "Update AGENTS.md for Auth..." }
]

That single message dispatches six concurrent conversations. The parent is still active too. Counting the parent, that's seven simultaneous streams against one account. None of them know about the others. The TPM bucket does.

The concurrency math nobody plans for

The token cost of a single sub-agent isn't its prompt. It's the prompt, plus the system prompt, plus the tool descriptions, plus the rendered CLAUDE.md and AGENTS.md files, plus whatever path-scoped rules the harness loaded for that agent's file context. On a real project that often lands between 15K and 50K tokens before the agent's first response.

Prompt caching helps. Anthropic caches the static prefix (system prompt, tool definitions, eager-loaded memory) so subsequent turns within the same conversation read the cached tokens at a discount. Cached reads still count against your TPM bucket. The pricing is lower, but the rate limit doesn't distinguish.

Here is the math for the dispatch above, assuming a modest 30K-token static context per sub-agent and 8 turns of back-and-forth before each finishes:

LayerPer-turn tokensTurnsSub-agentsTotal against TPM
Static cached context30,000861,440,000
Dynamic input (per-turn user content)4,00086192,000
Output tokens2,50086120,000
Parent agent (still running)8,00012196,000
Total billed against one account~1.85M
Roughly 1.85 million tokens through a single account over the lifetime of one fan-out. The bulk of it is cached input, which is exactly the part most developers don't track because cache reads feel free. They aren't free to the rate limiter — the [Anthropic docs are explicit](https://docs.anthropic.com/en/api/rate-limits) that cached and uncached input both count toward TPM.

The 5-hour rolling window then takes that 1.85M-token burst and amortizes it across the rest of your workday. One ambitious fan-out at 10am can starve your account at 1pm without any human-visible warning between those two times.

Why a single account chokes

Rate limits in Claude Code are per-account, not per-conversation. Anthropic enforces two ceilings simultaneously:

  • TPM (tokens per minute) — the total token throughput allowed in any rolling 60-second window
  • RPM (requests per minute) — the total number of API requests allowed in the same window

A six-way fan-out sends six concurrent streaming requests. Each streaming request opens, holds the connection, and dribbles tokens at the model's generation rate. From the API's view, you have six requests in flight, each consuming a fraction of your TPM continuously, none of them closing fast enough to free the bucket before the next batch lands.

The failure spec looks like this in practice:

  1. The first sub-agent's request opens and starts streaming. Fine.
  2. The next two open in the same 200ms window. Probably fine — TPM is rolling.
  3. The fourth opens and pushes the projected next-second consumption above the per-minute ceiling. The API returns 429 with a Retry-After header.
  4. The Claude Code parent receives the 429 and retries the failed sub-agent after the suggested delay.
  5. The fifth and sixth sub-agents, which queued behind the failures, retry too. They retry into the same exhausted bucket. They 429 again.
  6. The parent's watchdog times out after several minutes of retry chatter. The fan-out partially completes — three sub-agents returned useful work, three timed out with no result.
  7. The parent now has a half-populated TodoWrite plan and no clean way to resume.

The Anthropic-side observation is that you didn't exceed your overall plan quota. You exceeded the per-minute bucket within it. The 5-hour wall is a separate ceiling on top, and the weekly cap is a third ceiling on top of that. Sub-agent fan-outs trip the TPM ceiling first because they concentrate load in time.

This is the pattern users describe as "burned 50–70% of your 5-hour limit on one prompt." The "one prompt" was a fan-out. The 5-hour limit didn't break — the per-minute bucket inside it did.

Pool routing vs serial routing

There are two architectural responses. They solve the problem in different ways and one of them is a workaround.

Serial routing — throttle the parent. The parent agent dispatches sub-agents one at a time, waiting for each to complete before starting the next. The TPM bucket has time to drain between requests. No 429s, but the wall-clock cost is now N × the longest single sub-agent runtime. A six-way fan-out that would have finished in 4 minutes now takes 24. You also lose context-isolation benefits — the parent's narrative thread has to stay coherent across long serial waits.

Pool routing — route each sub-agent's traffic to a different Claude.ai account. The parent's six concurrent Task calls become six requests against six separate TPM buckets. Each account sees a single request and a normal token load. The fan-out completes in 4 minutes because the parallelism is real now — it isn't running into a shared ceiling.

ApproachWall-clock costConcurrencyRisk
Serial throttle6× slower1Watchdog timeouts on the parent; lost context coherence
Round-robin poolSame as fan-outNNaive routing can ping-pong between two near-cap accounts
Budget-aware poolSame as fan-outNNegligible — see anti-ping-pong section
Serial throttling is what most developers reach for first because it requires no infrastructure. It's a workaround in the literal sense: you're avoiding the bottleneck by going around it slowly. Pool routing is structural: you're removing the bottleneck by distributing load across the per-account buckets you already own.

Most working developers have two to four Claude.ai accounts already — a personal Pro, a work Pro or Max, sometimes a second personal account for experiments. Pool routing activates the accounts you already pay for. Serial routing pretends you only have one.

Balanced Mode does the routing for you — free to download and try, no credit card needed.

A realistic pattern: the enforce loop

Take a concrete scenario from the project I work in. The repo has a save-time enforcement pipeline that runs phpstan, eslint, and stylelint after every edit. When violations accumulate, an autonomous "fix every lint violation" loop spawns sub-agents — one per file or one per rule cluster — to drive the count to zero.

A typical session looks like this: the loop discovers 80 violations across 40 files. It groups them by domain and spawns a fix-agent for each group. With concurrency capped at 10, that's 10 sub-agents running, 70 queued, parent supervising.

On a single Pro account, this dies. Walk forward through the morning:

  • 9:00am — Loop starts. First 10 sub-agents dispatch. Two 429 immediately on the parallel cold-start. Parent throttles to four concurrent. Workable.
  • 10:15am — Fixes have been streaming in. Parent has consumed about half the 5-hour bucket. Every new sub-agent now opens against a TPM that's already 60% utilized by long-running siblings.
  • 11:00am — 5-hour rolling-window utilization crosses 90%. New sub-agents take 30-second 429 backoffs before their first turn. The fix rate falls to one violation per 90 seconds.
  • 11:25am — Rate-limit wall. Account is throttled to a trickle until the rolling window expires hours later. The remaining 35 violations are blocked.

Same loop, pooled across four accounts:

  • 9:00am — Loop starts. The router places sub-agents on accounts A, B, C, D in turn. Each account sees a single concurrent request.
  • 10:15am — Account A is at 30% of its 5h window. Account B at 28%. Accounts C and D similar. The pool's effective ceiling is the sum.
  • 11:00am — Mid-session. Each account around 50%. No 429s because no account has been asked to do more than a quarter of the work.
  • 12:30pm — Loop completes. All 80 violations fixed. No account exceeded 65% of its window.

The single-account variant doesn't fail because the work is too expensive. It fails because the work is concentrated against one bucket. Pooling solves it by giving the bucket more lanes — same total token spend, distributed across the accounts the developer was already paying for.

When you hit this on your own enforce loop, the 14-day Premium Trial is $0 today — Balanced Mode, the watchdog, and usage analytics included. Cancel anytime before day 15.

Anti-ping-pong: smart routing matters

A naive pool router places each sub-agent on whichever account has the most recent 429-free response. It looks like load-balancing. It isn't.

The failure case: two accounts, both at 85% of their per-minute window. Account A 429s on the first sub-agent. The router moves to account B. Account B 429s on the next sub-agent because its bucket is also nearly full. Router goes back to A. A is still throttled. The work stalls in a ping-pong loop between two saturated accounts, neither given enough breathing room to recover.

A budget-aware router doesn't react to 429s after they happen. It estimates projected remaining budget per account before placing each request and avoids any account whose projected next-minute consumption would push it over its limit. The projection uses the account's recent token-rate history, the request's estimated output size, and the account's published TPM ceiling.

When all accounts in the pool are projected to exceed their budget, the router queues the request behind whichever account will free up soonest — measured from the Retry-After headers it's already collected. The result is that the pool degrades gracefully under heavy load instead of ping-ponging two near-capped accounts against each other.

Smart routing is the difference between a pool that holds up under a Monday morning enforce loop and a pool that collapses into 429 chatter the moment any single account gets warm. The math isn't complicated — but the difference between a router that does it and a router that doesn't is the difference between a fan-out that finishes and one that strands a half-populated TodoWrite plan at noon.

Frequently asked questions

Does spawning sub-agents in parallel actually save wall-clock time if they all hit rate limits?

No. The benefit of parallel sub-agents assumes the underlying API can serve them in parallel. On a single account, the TPM bucket converts your parallelism into serialized retries. A six-way fan-out that 429s halfway through takes longer than a serial dispatch would have — you pay the latency of retries on top of the work itself. Parallelism is real only when the rate-limit budget is real, which means either a quiet account or a pool of them.

Does prompt caching reduce sub-agent token cost?

It reduces billed cost, not rate-limit pressure. Anthropic's cached input is priced lower than fresh input, but cached reads still count against your TPM ceiling. A sub-agent that loads 30K cached tokens on every turn is still drawing 30K tokens per turn through the per-minute bucket. Caching saves money over a long session; it does not let you fan out further before hitting a 429.

What's the difference between TPM and RPM limits and which one trips first on fan-out?

TPM is tokens per minute, RPM is requests per minute. Both are enforced concurrently per account. For sub-agent fan-outs, TPM trips first in almost every case — each sub-agent is one request but tens of thousands of tokens, and the request count rarely reaches the RPM ceiling before the token volume hits the TPM ceiling. RPM matters more for high-frequency one-shot tool calls, less for long-running agent conversations.

Can I just throttle my parent agent to spawn sub-agents serially?

You can, and many developers do as a first response. It works for small fan-outs. The cost is that wall-clock time scales linearly with the number of sub-agents, and the parent's watchdog can time out on long serial chains. You also lose the context-isolation benefit — if the sub-agents are sequential, the parent has to hold their results in its own context while waiting, which defeats one of the reasons to spawn them in the first place.

Does pooling work with the Task tool, or only with direct Claude Code calls?

Pooling works at the API-routing layer, beneath the Task tool. Claude Code's Task spawn produces an API request; the router intercepts that request and places it on the appropriate account in the pool. The Task tool itself doesn't need to know — it sees a normal completion. The implementation matters: route at the request level (so each sub-agent gets its own account assignment), not at the session level (which would force the whole parent and all its children onto one account).

Closing CTA

If you're running heavy sub-agent fan-outs against a single Claude.ai account, the failure mode above is going to find you eventually. The route around it is structural — distribute the load across the accounts you already pay for, with a budget-aware router that doesn't ping-pong two near-capped accounts. That's what the rotation extension we ship does for the Task tool out of the box.

The 14-day Premium Trial is $0 today — you get Balanced Mode, the anti-ping-pong router, and usage analytics. Cancel anytime before day 15. Prefer not to put a card down? Install Power Claude Free: the 7-day free trial includes full Pro access, no credit card required.