Why monthly caps don't protect you from one bad LLM run
An agent starts a task at night. A retry loop gets stuck. By morning the bill is many times the monthly cap that was supposed to prevent exactly this.
The cap didn't fire. The bill did.
This is not a bug. It's how monthly caps work. And if you're building AI agents in production, it will happen to you too, unless you change the pattern.
The timeline of a bad run
The shape of the failure is always the same:
11:30pm, agent starts a research task. Fetches a URL. Gets a timeout. Retries. Gets another timeout. The retry logic calls the LLM to decide what to do next. The LLM decides to retry again. This repeats.
The monthly cap is passed somewhere around midnight. But the cap check runs on a billing cycle, not on each request, so nothing stops. The agent keeps looping until someone wakes up and kills it, thousands of calls later.
Monthly caps are accounting tools. They tell you what happened. They don't stop anything from happening.
Why the cap didn't fire
Most billing systems, OpenAI's included, check spend limits asynchronously. The request goes through first. The ledger updates after. By the time the cap logic runs, hundreds more requests have already been processed.
This is a fundamental property of post-hoc billing, not a bug you can patch. The cap will always lag behind the actual spend, especially during a loop that fires hundreds of requests per minute.
A monthly cap and a bill many times its size can coexist. They operate at different time scales.
The pattern that actually works: preflight
The fix is to check budget before the run starts, not after it finishes. This is called a preflight check.
Before your agent makes a single API call, you ask: does this customer have budget for this run? If not, you block it. The agent never starts. No tokens consumed. No bill generated.
from agentbill import AgentBillClient client = AgentBillClient(api_key="agb_your_key") # Before the agent runs: reserve the units this run expects to cost. # A blocked run raises here, before anything expensive happens. client.preflight(agent_id="researcher", estimated_units=200) # Agent only runs if budget is confirmed result = run_my_agent() # Record the run: the same units preflight reserved (the server settles by the recorded amount) client.record(agent_id="researcher", units=200)
Two calls. The agent either runs with a confirmed budget or it doesn't run at all. No overnight surprises.
Monthly caps vs. per-request ceilings
These solve different problems. A monthly cap is useful for overall budget visibility, you want to know your AI costs didn't triple this month. Fine.
A per-request ceiling is what protects you from a single bad run. It operates at the invocation level, before compute is consumed, with no lag between the check and the block.
You need both. The monthly cap catches drift. The preflight ceiling catches catastrophe.
The same run, replayed with preflight
Same agent. Same retry bug. Same overnight run.
First invocation: preflight checks the task budget. Approved, units remain. Agent runs. Finishes. Cost recorded.
Second invocation (the retry loop): preflight checks again. Previous run already consumed the budget for this session. Blocked. Agent never starts.
The run stops at the ceiling you set, not at whatever the loop reaches by morning.
The retry bug still exists. But it can't compound into a runaway loop when each invocation requires a budget check to proceed.
Implementing preflight in your stack
The pattern works regardless of what's inside your agent, LangChain, OpenAI Agents SDK, AutoGen, custom chains. You're wrapping the invocation, not the internals.
Python:
pip install agentbill-sdk
from agentbill import AgentBillClient, BudgetExhaustedError
client = AgentBillClient(api_key="agb_your_key")
def run_agent_safely(customer_id: str, task: str):
try:
client.preflight(agent_id="my_agent", estimated_units=200, customer_id=customer_id)
except BudgetExhaustedError as e:
return {"blocked": True, "reason": str(e)}
result = run_my_agent(task)
client.record(agent_id="my_agent", units=200, customer_id=customer_id)
return result
Node.js:
npm install agentbill
import { preflight, record, BudgetExhaustedError } from 'agentbill' // reads AGENTBILL_API_KEY
async function runAgentSafely(customerId: string, task: string) {
try {
await preflight({ agentId: 'my_agent', estimatedUnits: 200, customerId })
} catch (e) {
if (e instanceof BudgetExhaustedError) return { blocked: true, reason: e.message }
throw e
}
const result = await runMyAgent(task)
await record({ agentId: 'my_agent', units: 200, customerId })
return result
}
Summary
Monthly caps are accounting. Preflight checks are protection. One tells you what happened; the other prevents it from happening.
If you're running AI agents in production, especially agents that loop, retry, or run unattended, you need a check that fires before the first token, not after the last one.
Add preflight to your agents
Free tier: 1,000 preflight calls/month. No credit card required.