Rate Limiting AI Agents to Prevent Bot Abuse
Turn a 3am retry storm into a non-event.
- An uncapped retry loop can run an agent 10,000× overnight before anyone notices.
- A rejected request costs ~0; a processed one costs an execution plus a model call.
- A token bucket absorbs a human burst while capping the sustainable average.
The problem
- Every uncapped request is a real execution and a real model call.
- Retry storms, bot abuse and runaway loops run at full speed and full cost.
- An integration stuck in a loop overnight is an unbounded bill before anyone's awake.
The solution
Pick an algorithm, set a rate, and enforce it in front of the agent, not inside it.
- Default to a token bucket — it absorbs a short human burst, caps the sustainable average.
- Limit per sender (stop one abuser) and globally (survive a broad spike).
- Reject the excess with 429 + Retry-After so good clients back off.
A rejected request costs almost nothing; a processed one costs an execution plus a model call.
Result: the 3am flood is throttled, not billed.
The incident always happens at 3am: a misconfigured integration starts retrying in a loop, or a bot discovers your webhook, and by morning your AI agent has run ten thousand times and your bill has a new digit. Rate limiting is the control that turns that night into a non-event. Here is how to do it right, and the two places you can enforce it.
What rate limiting protects against
Rate limiting caps how many requests a sender can make in a given period. Three sources produce the floods it stops: retry storms (a downstream failure makes an integration retry endlessly), bot abuse (an automated client hammers your public webhook), and runaway loops (two systems trigger each other in a cycle nobody noticed until the invoice arrived). Without a limit, all three run at full speed and full cost. With one, the excess is rejected cheaply instead of processed expensively.
Three rate-limiting algorithms
The right choice depends on how bursty your legitimate traffic is.
| Algorithm | How it works | Best for | Weakness |
|---|---|---|---|
| Fixed window | N requests per clock interval | Simple, low-traffic agents | Bursts at window edges |
| Sliding window | N requests per rolling interval | Smoother enforcement | Slightly more state |
| Token bucket | Tokens refill at a steady rate; each request spends one | Bursty but bounded traffic | Needs tuning |
Fixed window is simplest: allow N per minute, reset on the minute — but a sender can fire N at 0
and N more at 1, doubling the rate for an instant. Sliding window counts over the last rolling interval, smoothing that edge at the cost of a little more bookkeeping. Token bucket holds tokens that refill at a steady rate and each request spends one — it allows a short legitimate burst while enforcing a sustainable average. For AI agents with naturally bursty human traffic, it’s usually the best fit.When a sender exceeds the limit, reject with 429 Too Many Requests and, where you can, a Retry-After header — the standard signal well-behaved clients understand.
The manual fix (Option 1)
You can implement any of these in your own middleware — but the state is where it gets hard.
Fix 1: pick an algorithm and enforce it
A token bucket for most agents, sized above your real per-sender peak, then tightened.
On a gateway, the algorithm and rate are choices you set, not code you ship.
Fix 2: back it with a shared, fast counter store
The limit needs a store for counters (per sender, per window) that’s fast, shared across instances, and correct under concurrency.
On a gateway, the counter state is managed for you, in front of everything.
The catch: you replicate it at every entry point
Every channel and workflow your agent exposes needs the same limiter, sharing the same counters — so the DIY path is not one limiter but a distributed system you own and keep correct as you add channels.
Place a gateway layer (Option 2) — faster
A message gateway applies the limit once, in front of everything, with the counter state managed for you. You choose the algorithm and the rate; the gateway enforces it on every channel. It runs alongside token budgeting, its natural partner against oversized input — see token floods.
Option 1 (DIY) vs Option 2 (gateway)
| Do it yourself | Use a gateway | |
|---|---|---|
| Choose algorithm & rate | ✓Full control | ✓A setting you pick |
| Shared counter store | ✕You build & host | ✓Managed for you |
| Concurrency under load | ~Yours to get right | ✓Handled |
| Every entry point | ~Replicate the limiter | ✓One layer, all channels |
| Reject cost (429) | ✓Near zero | ✓Near zero, before the agent |
| Control of internals | ✓Full | ~Abstracted away |
The full set of input-layer defenses is in the complete guide to message quality for AI agents.
If option 2 sounds more like you, here’s the short version of how to set it up: cap abusive senders before the agent →.
Either way — go make a 3am retry storm a non-event.
Thanks for reading. 👋
Orlando. -
Orlando Quijada
Technologist with 15+ years of experience. Developer, SaaS tester, and AI collaboration & optimization specialist. Currently contributing at portarus.com.
Written on