Why every agent needs a cost cap on day one
Three numbers, one enforcement point, and a hard ceiling before the bill lands. The cheapest pattern that separates an agent you run from one you babysit.

The day an agent cost $80 in an hour is the day you stop trusting “we’ll watch it.” The pattern is the same every time: an infinite-loop prompt kept calling a frontier model with a large context window, the loop ran 200 times before anyone noticed, and the bill landed before the alert.
A cost cap is not a fancy observability feature. It is the difference between an agent you run and an agent you babysit. The cost-cap pattern is the one every metered API recommends — OpenAI’s rate limits guide, Anthropic’s rate limits, AWS billing alarms, and Stripe’s usage-based billing alerts — the industry converges on hard ceiling plus soft warning.
Before you start
You need:
- A Python function that calls your model of choice (any framework).
- A writable directory for the JSONL ledger (any path your agent can append to).
- A way to inspect the ledger (
wc -l,awk, or your editor of choice).
No cloud account, no paid service, no special library.
The minimum viable cost cap
You need three numbers and one enforcement point.
- per-run ceiling $1.00
- per-day ceiling $5.00
- per-week ceiling $25.00
The enforcement point is before the model call, not after. After is a bill. Before is a circuit breaker.
# pseudo, fits any framework
def call_model(prompt, run_id):
cost_so_far = ledger.cost(run_id=run_id, scope="run")
if cost_so_far >= RUN_CEILING:
raise CostCeiling("run", RUN_CEILING)
cost_today = ledger.cost(scope="day")
if cost_today >= DAY_CEILING:
raise CostCeiling("day", DAY_CEILING)
cost_week = ledger.cost(scope="week")
if cost_week >= WEEK_CEILING:
raise CostCeiling("week", WEEK_CEILING)
response = model.complete(prompt)
ledger.record(run_id, model, response.tokens, response.cost)
return response
That is the whole thing. A ledger, three ceilings, one check.
| Command | Job | Success state |
|---|---|---|
RUN_CEILING = 1.00 (constant) | Sets per-run dollar cap. | Editing the constant changes the per-run limit; reload takes effect on next run. |
DAY_CEILING = 5.00 | Sets per-day dollar cap. | Same — change at the top of the file. |
WEEK_CEILING = 25.00 | Sets per-week dollar cap. | Same. |
ledger.cost(scope=...) | Reads accumulated cost for the scope. | Returns a float in USD; raises if ledger is missing. |
raise CostCeiling(scope, value) | Stops the call before it happens. | Caller sees the exception and the value that triggered it; ledger is unchanged. |
Hard ceiling vs soft warning
Two flavors, both useful. The OpenAI rate limits guide and the AWS billing alarms docs both describe this split.
- Hard ceiling: the call is refused. No exceptions. Use this for
per-dayandper-week. - Soft warning: the call goes through, but the agent is told “you are at 80% of today’s cap.” Use this for
per-runso the agent can finish a legitimate task without being cut off mid-stream.
if cost_so_far >= RUN_CEILING * 0.8:
log_warning(f"run at {cost_so_far/RUN_CEILING:.0%} of cap")
if cost_so_far >= RUN_CEILING:
raise CostCeiling(...)
What the ledger looks like
A tiny JSONL append-only log is enough.
{"ts":"2026-07-20T13:11:04Z","run_id":"7c2a","model":"claude-sonnet-4","tokens_in":482,"tokens_out":124,"cost_usd":0.0014}
{"ts":"2026-07-20T13:11:09Z","run_id":"7c2a","model":"claude-sonnet-4","tokens_in":211,"tokens_out":38,"cost_usd":0.0006}
A run-end line rolls up totals.
{"ts":"2026-07-20T13:11:14Z","run_id":"7c2a","kind":"end","total_cost_usd":0.0022,"status":"ok"}
You do not need a database. You need wc -l and awk '{s+=$NF} END{print s}'.
The 1-minute review
After a week of running with the cap, answer these in 60 seconds:
1. What was my most expensive run this week? (probably an image/video gen burst)
2. What model was it? (probably a frontier one I called by accident)
3. Did the cap save me anything? (almost certainly yes)
If the answer to #3 is no, your ceilings are too high. Lower them until the cap fires at least once a week. A cap that never fires is not a cap, it is a number.
How to know it is working
The cap is working when CostCeiling fires at least once a week. The cap is also working when the agent sees the 80% soft warning and finishes its task anyway. The cap is not working if no exception has fired in 30 days — the ceilings are too loose, or your agent is not running.
Common failures and fixes
| Failure | Cause | Fix |
|---|---|---|
| The cap never fires. | Ceilings too high; agent not running. | Lower ceilings until the cap fires at least weekly. |
| The cap fires on a legitimate task. | Per-run ceiling too tight for the model you call. | Move that model class to per-day scope; raise per-run to $2 or $5. |
| The ledger has gaps. | Agent crashed before recording. | Add try/finally around the ledger write. |
| CostCeiling is raised but the next call still goes through. | Caller catches the exception and retries. | Hard-fail at the entry point, not in the caller’s retry loop. |
When not to use this pattern
- Sub-cent runs at low volume. If your agent runs five calls a day on a cheap model, the ceiling logic is overhead, not protection.
- Models you trust and have already cost-capped at the provider. If your OpenAI org has a $50/day hard limit set in the dashboard, your local ceiling can be a soft warning only.
- Anything where “fail the call” would corrupt state. A long-running migration that aborts mid-write is worse than the bill. Set a checkpoint-and-resume pattern first, then a ceiling.
Updates and breaking changes
Re-run the 1-minute review after any of these:
- You switch model providers (cost ceilings are model-specific).
- You add a new tool that calls a paid API (image gen, transcription, web search).
- Your provider changes its per-token pricing — re-read OpenAI pricing or the equivalent page monthly; ceilings that were loose last quarter may be tight this quarter.
A ceiling tuned six months ago is a guess today. Verify it.
Where to get unstuck
- OpenAI: the rate limits guide is the authoritative reference; if you hit a 429 you cannot explain, file a ticket with the
Request-IDfrom the response headers. - Anthropic: rate limits page lists tier-by-tier limits; the help center accepts tier-increase requests.
- Independent: HN threads on agent cost-runaway loops — multiple “my agent burned $X in an hour” stories; consistent recommendation is “hard ceiling from day one.”
- File a bug: include your ledger path, the run_id of the failed call, the model name, and the dollar amount that slipped through.
Sources
- OpenAI — Rate limits guide — used for: the “hard ceiling” pattern, 429 response semantics, tier limits. Verified 2026-07-22 via web_extract (canonical URL returned 200).
- OpenAI — Pricing — used for: per-model token costs that determine where to set ceilings. Verified 2026-07-22.
- Anthropic — Rate limits — used for: tier limits and 429 interpretation on Anthropic’s side. Verified 2026-07-22.
- AWS — Billing alarms for estimated charges — used for: the cloud-side equivalent of the agent-side ceiling pattern (corrected from original draft’s typo’d URL). Verified 2026-07-22.
- Stripe — Usage-based billing alerts — used for: industry-standard pattern for capping spend on metered services. Verified 2026-07-22.
- HN Algolia — agent cost runaway — used for: independent builder confirmation of the “hard ceiling from day one” consensus. Verified 2026-07-22.
Last verified: 2026-07-22. Reviewer lineage: drafter and fact-checker are the same model (MiniMax-M3). Where a quote is paraphrased from memory rather than retrieved from the source, the paraphrase was cross-checked against the canonical URL on 2026-07-22.



Submit a take
Have a different read on this? Drop a comment below — your email isn't published, and I read every one. Nothing leaves the site until I approve it.