Twilio · AI agent security · Spend control
Twilio AI agent rate limit: four controls to prevent a $1,200 SMS bill
At US domestic SMS rates, a stuck agent loop sends roughly 2,500 messages before it hits $100. At UK rates, roughly 750. Twilio's native rate limits are per-number, not per-agent — they don't care how fast your application logic fires. This page covers the four controls that add a real spending ceiling before the damage accumulates.
TL;DR
Twilio doesn't have a per-agent spend limit. Four controls fill the gap, ordered from weakest to strongest: Messaging Services daily volume caps (capped per Messaging Service, fires 429 when exceeded), sub-account budget limits (Twilio account-level, stops new resource creation when exceeded), proxy-layer pre-call enforcement (fires before the call, zero lag, per-agent), and circuit-breaker in agent loop (cooperative — only works if the agent code checks it). For production money-moving agents, use proxy enforcement as the primary control with a Messaging Service cap as a backup.
Why Twilio's native rate limits don't help
Twilio enforces two kinds of native rate limits:
- Per-number throughput limits: a 10DLC registered long-code can send roughly 3 message segments per second. A toll-free number is ~3 segments/second. These limits exist to prevent carrier filtering, not to protect your wallet.
- Account-level concurrency: there's a default cap on concurrent API calls, but it's high enough that most applications never hit it.
Neither of these is a dollar cap. At 3 messages/second, a stuck agent loop burns through $100 at US domestic rates (~$0.0079/SMS) in about 55 minutes. At UK rates (~$0.042/SMS), in about 10 minutes. Twilio's spend alert email — configured under Billing → Spend Thresholds — is typically delivered 4+ hours after the threshold is crossed. By then, the damage is done.
Control 1 — Messaging Services daily volume cap (weakest)
Twilio's Messaging Services have a "Max Daily Messages" setting (available in the Messaging Service configuration). When the limit is reached, Twilio starts queuing outbound messages and eventually returns a 429. This is a count cap, not a dollar cap — and it's per Messaging Service, not per agent.
When it fires: after the cap is exceeded. Messages up to the limit are sent; messages over the limit are queued or dropped depending on queue configuration.
Gap: if you route all your agents through the same Messaging Service, one agent's runaway loop consumes the cap for all agents. You'd need one Messaging Service per agent for true isolation — which defeats the operational simplicity of the feature.
Best for: a simple soft ceiling on a single-agent, single-Messaging-Service deployment.
Control 2 — Sub-account with a balance limit
Twilio lets you create sub-accounts under a master account. Each sub-account has its own credentials, its own phone numbers, and — critically — its own balance. You can fund a sub-account with a fixed balance and never add more. When the balance hits zero, the sub-account's calls fail.
When it fires: when the sub-account balance is exhausted. All calls from that sub-account start returning 400 errors.
Gap: sub-account balance is a lifetime limit, not a daily one. If you fund it with $50 and the agent loop drains it in 20 minutes, the limit held — but you have to manually top it up for legitimate subsequent use. Also: creating and managing one sub-account per agent class adds operational overhead.
Best for: strict isolation between production and staging environments, or between customer-facing agents with very different risk profiles.
Control 3 — Proxy-layer pre-call enforcement (strongest)
A proxy between your agent and the Twilio REST API can enforce a daily dollar cap before forwarding the call. The agent calls proxy.keybrake.com/twilio/2010-04-01 with a vault key instead of calling api.twilio.com with the real Auth Token. The proxy looks up the vault key's policy, checks the current day's spend against the cap, and either forwards (charging the running total) or rejects with 429 before the call reaches Twilio.
from twilio.rest import Client
# One-line change: base_url points to the proxy
client = Client(
username=os.environ["VAULT_KEY"], # vault_key_xxx
password="unused",
base_url="https://proxy.keybrake.com/twilio"
)
# Agent code unchanged
client.messages.create(
body=message_body,
from_="+15005550006",
to=recipient_phone
)
The vault key policy on the Keybrake dashboard:
{
"vendor": "twilio",
"daily_usd_cap": 50,
"allowed_endpoints": ["POST /2010-04-01/Accounts/*/Messages"],
"expires_in": "8h"
}
When it fires: before the call is forwarded — zero-lag. The call that would exceed the cap is rejected with 429 before Twilio sees it. Twilio never charges for the rejected call.
Why this is the strongest control: it fires before spend, not after. And it's per-agent (per vault key), not per-Messaging-Service or per-account. You can run 10 different agents with 10 different caps, each isolated, on the same underlying Twilio account.
Control 4 — Circuit-breaker in the agent loop (cooperative)
A circuit-breaker is a flag in your code that the agent checks before each outbound SMS call. If the flag is tripped (by hitting a daily counter or by an external signal), the agent skips the call.
class TwilioSendSMSTool(BaseTool):
name = "send_sms"
description = "Send an SMS via Twilio."
def _run(self, to: str, body: str) -> str:
if self._daily_count() >= MAX_DAILY_SMS:
return "Daily SMS limit reached. Aborting."
client.messages.create(body=body, from_=FROM_NUMBER, to=to)
self._increment_count()
return "SMS sent."
When it fires: on the next call after the counter is checked — next-request-fast. But the counter lives in memory (or Redis), which means it resets on restart and doesn't account for concurrent agent instances.
Gap: cooperative. If the agent uses a third-party tool or SDK that calls Twilio directly (e.g., a Twilio MCP server, or a tool you didn't write), the circuit-breaker is bypassed. Not enforced at the network layer.
Control comparison
| Control | Fires when | Per-agent? | Dollar-accurate? | Survives restart? |
|---|---|---|---|---|
| Messaging Service cap | After cap hit (count-based) | Per-Service | No (count, not $) | Yes |
| Sub-account balance | After balance exhausted | Per sub-account | Yes (lifetime balance) | Yes |
| Proxy enforcement | Before call (pre-spend) | Yes (per vault key) | Yes | Yes |
| Circuit-breaker flag | On next call check | If counter is per-agent | No (count-based typically) | Only if persistent store |
Practical layering for a production Twilio agent
For a production agent that sends transactional SMS:
- Primary: proxy-layer enforcement with a per-agent daily $ cap (vault key with
daily_usd_cap). This fires before spend, handles concurrent instances correctly, and survives restarts. - Secondary: Messaging Service daily volume cap set at 2× the expected daily volume. Acts as a catch-all if the proxy misconfigures or the agent switches to a direct Twilio call path.
- Tertiary: Twilio spend threshold alert (under Billing) set at 3× the expected daily spend. Fires after the fact, but gives you a human-readable alert for the monthly billing review.
How Keybrake fits
Keybrake is the proxy in Control 3. You issue a vault key per agent, configure the Twilio daily cap and endpoint allowlist in the dashboard, and change the Twilio SDK's base URL. The Free tier covers 1,000 proxied calls/month; the Hobby tier ($29/month) adds all three vendors (Stripe, Twilio, Resend) and email alerts on cap breach.
Related questions
Does this work with Twilio's Conversations API as well as the Messages API?
Yes. The proxy forwards to api.twilio.com and the vault key policy specifies which endpoint paths are allowed. You can allow POST /2010-04-01/Accounts/*/Messages for the Messages API and separately allow POST /v1/Conversations/*/Messages for the Conversations API — or restrict to one of the two. The same daily dollar cap applies across both since it's policy-level, not path-level.
What does the proxy cost for high-volume agents?
Keybrake's Hobby tier includes 10,000 proxied requests/month at $29/month. At Twilio US domestic SMS rates ($0.0079/message), 10,000 messages costs about $79 in Twilio spend — so the proxy adds ~$0.0029/message in overhead. For agents where a single stuck-loop incident would cost hundreds of dollars, the overhead is negligible.
Can I use a sub-account AND a proxy together?
Yes, and it's the belt-and-suspenders approach. The proxy enforces the per-agent cap at call time; the sub-account balance provides a hard ceiling if the proxy has a bug or is bypassed. The sub-account balance should be set to the maximum tolerable weekly spend — it's a lifetime limit, so size it accordingly.
Further reading
- AI agent Twilio security: the complete guide — the 1,600-word deep dive on all four controls with implementation code.
- Budget alerts for AI agents: four patterns ranked by how late they fire — where Twilio spend threshold alerts sit in the full alert-latency comparison.
- AI agent cost management — how LLM cost (LiteLLM's domain), SaaS-tool cost (ours), and infra cost are scoped differently.
- AI agent kill switch patterns — what to do the moment you realize the agent is running away.