What Happens When You Rate-Limit an AI Agent? It Doesn't Wait — It Swarms.
Day 6: Rate Limiting in the Age of Agentic Callers
Rate limiters have quietly protected our APIs for well over a decade on one unwritten assumption: the caller waits after a 429. Agents don't wait — they replan. Swap out that single assumption and a limiter that's run fine for years turns into the thing that takes your infra down.
The incident
This particular limiter hadn't been touched in years — nobody had a reason to. Token bucket per API key, a 429 with a Retry-After header, nothing clever. Then we shipped an agent integration on top of it: a planner allowed to call a handful of internal tools to get a task done. Alarms went off inside ten minutes.
What mattered wasn't the request-volume graph — it was retry count over time. Normal traffic draws a flat, boring line. Ours went from flat to nearly vertical about three minutes after the first 429, and kept climbing well past where any sane backoff should have flattened it out. That shape, not the alert itself, told us what we were dealing with before we'd even started on root cause.
What the limiter was built to protect against
Every 429/Retry-After design carries an unwritten contract: the client waits its turn. It was built for two failure modes — bursty humans hammering refresh, and misbehaving scripts with linear or exponential backoff. In both, a 429 is a rare, terminal event for that request: the client gives up, or waits and tries once more. The limiter's job was to smooth out a spike, not to survive a caller that treats rejection as a reason to try harder.
What actually happened
The agent never read the 429 as a rate-limit signal. It saw a failed tool call. And to an LLM-driven planner, a failed tool call is just an obstacle to route around — so it replanned. Replanning didn't mean 'wait, then retry the same call.' It meant 'find another way to hit the sub-goal,' which included firing off parallel sub-calls to improve the odds one of them landed.
- ▹One throttled request turned into N retries from the same agent
- ▹Each of those retries hit the same wall, which triggered another round of replanning
- ▹Replanning fanned out into parallel sub-agent calls — 'try harder' baked straight into the orchestration layer
- ▹Each sub-call could retry on its own, so growth wasn't linear — it was closer to exponential
- ▹None of it looked like 'one client retrying too fast.' It looked like a swarm.
Why raising the limit doesn't fix it
The instinct is to bump the bucket size, raise the rate. That buys time, not safety — it just pushes the collapse further out, because the growth is structural, not volumetric. One 'agent' isn't one caller. It's the root of an unbounded call tree that can branch every time a sub-goal fails. Limiting by identity — API key, user ID — only ever sees a single node of that tree at a time. By the time enough nodes have tripped the limit for you to notice, the storm is already inside your infrastructure, not waiting politely at the door.
The reframe: key on lineage, enforce backoff, don't suggest it
For agentic callers, identity has to mean the whole task or session lineage — the entire call tree — not just the credential attached to a single request. And Retry-After can't stay advisory. It has to be an instruction the orchestrator is structurally forced to obey, not a number a retry wrapper glances at and maybe respects.
# Old contract (human/script assumption)
client -> 429 + Retry-After: 5
client waits ~5s, retries once
# Agentic contract (what you actually need)
request header: X-Task-Id: <root task id>
X-Lineage: <root>.<planner>.<subagent>
limiter keys on X-Task-Id, not just API key
-> counts EVERY node under that root as one caller
orchestrator enforces Retry-After at the scheduling layer:
- blocks new sub-calls under that lineage until Retry-After elapses
- does NOT let replanning bypass the wait by trying a 'different' path
- caps max concurrent sub-calls spawned per replan (fan-out ceiling)Concretely: thread a task or session ID through every sub-call so the limiter can see the tree, not just the leaf. Make Retry-After a hard gate inside the orchestrator's scheduler — the same layer that decides whether a sub-agent gets to fire at all — not a detail buried in an HTTP client's retry logic the planner never even sees. And cap how many sub-calls a single replan can spawn, so 'try harder' hits a floor it can't dig through.
Where this sits in the glossary
Rate limiting was never just an API-protection primitive. Quietly, it was already a governor on how much any one actor could do to your system per unit of time. In an agentic system, that governor stops being routine infrastructure hygiene and becomes a governor on autonomy itself — one of the few levers you have to bound what an agent is allowed to attempt, independent of how well it reasons. Tomorrow's concept builds directly on this idea of bounding agent behavior.
The checklist
Before you let an agent call anything behind your limiter, check three things:
- ▹Keying: does the limiter see the task/session lineage, or only the API key/user ID? If it can't tell 'one caller' from 'one call tree,' it can't see the storm forming.
- ▹Backoff enforcement: is Retry-After enforced at the orchestrator's scheduling layer, or just read by a retry wrapper the planner can route around by replanning?
- ▹Fan-out caps: is there a hard ceiling on how many sub-calls a single replan can spawn? Without one, 'try harder' has no floor.
Extend your knowledge
- ▹Trace how your own orchestrator handles a 429 today — is Retry-After enforced at the scheduler, or just read inside a retry wrapper?
- ▹Check whether your rate limiter has any concept of task/session lineage, or whether every sub-call looks identity-identical to it.
- ▹Read up on distributed rate limiting patterns — token bucket vs. sliding window — and work out which one actually supports a lineage key without a redesign.
- ▹If you run multi-agent systems, graph retry count against time as its own metric. It'll flag a fan-out storm minutes before a generic error-rate alert would.
Discussion
Chat with Chi Cong (AI) about this article. Your conversation is private to you — you can publish a summary for others when you're done.