Get your API key →

One ceiling per task_ref. Not per project, not per calendar month.

Every call sharing a task_ref consults one ceiling before it runs, in units you define. The call that would cross it gets approved: false.

pip install agentbill-sdk

free tier1,000 preflight calls/mono cardkey in 30 seconds

the whole integration
from agentbill import AgentBillClient

client = AgentBillClient(
    api_key="agb_your_key")

# You decide what a unit is worth.
# job-142 gets 500 of them, across
# every call passing this task_ref.
client.preflight(agent_id="researcher",
                 task_ref="job-142",
                 task_ceiling=500,
                 estimated_units=12)

# your provider call goes here

# settle, or the units stay held
# until the reservation expires.
client.record(agent_id="researcher",
              task_ref="job-142",
              units=12)

>>> run 42 of the retry loop:
POST /preflight200 · refused
{ "approved": false, "reason": "task_ceiling_exceeded", "estimated_units": 12, "task_ref": "job-142", "task_ceiling": 500, "task_used_units": 492, "task_remaining_units": 8 }

The SDK raises TaskCeilingExceededError. Your code decides what happens next.

Try it · runs in your browser

Run an agent into its ceiling.

Set a ceiling for the whole job and run the agent. The call that would cross it gets approved: false, with the same body your SDK gets.

taskjob-142
500units
Agent calls0 calls
0 used of 500 units
the planunitsrunning
·search.web1212
·fetch.page3143
·llm.summarize140183
·fetch.page28211
·llm.extract160371
·llm.rerank121492
·llm.critique180672
·llm.replan210882
·fetch.page26908
·llm.summarize1501,058
this plan asks for 1,058 units.
POST /preflightrequest
{ "agent_id": "researcher", "task_ref": "job-142", "task_ceiling": 500, "estimated_units": 12 }
the rule: used + reserved + estimated <= ceiling
The plan above is what the agent will send. Pressing Run executes it in your browser, against no account.
Same rule and same response body as POST /preflight.

Why a task, not a month

Your job is not an account, and it does not last a month.

The provider cap is real and it fires. It is bound to a project, to an organization over a calendar month, or to one session on the vendor's own harness. A run too small to move a monthly number never trips it. A number low enough to catch that run stops every agent in the organization until the month turns.

OpenAI · spend limits

A hard limit answers 429 project_spend_limit_exceeded, and enforcement “is not instantaneous, so recorded spend can slightly exceed the configured amount.” The boundary is the project or the organization.

developers.openai.com
Anthropic · rate limits

A tier spend cap pauses API usage “until 00:00 UTC on the first day of the next month.” The boundary is the organization, and the clock is the calendar.

platform.claude.com
claude-code · issue 64744, open

Enterprise plan, $3,000/month limit: “~$300 of unintended API usage over a single weekend with no way to detect or stop it from the CLI.” The limit is a month. The incident was a weekend.

github.com

Read at source on 2026-09-07. What differs is not whether a cap fires. It is what the cap is bound to.

Per-task ceilings

One job, many calls, one ceiling.

Pass task_ceiling on the first call and the same task_ref on every call after it. The first call fixes the ceiling. Each one consults it before it runs, in units you define.

Watch budgets burn down →
Task budgetsone job, many calls, one ceiling
job-8871 researcher close
492 / 500 units
job-8870 summarizer running
96 / 200 units · 12 reserved
nightly-crawl crawler close
1,840 / 2,000 units · 60 reserved
Sample rows, the same ones the demo console shows. Units are an integer you define.

No proxy

Two calls. Nothing in your request path.

preflight before your provider call, record after it. No base URL to change, no traffic routed through us, no provider keys held. If we are unreachable, the SDK raises inside your process and your code decides.

How the reservation works →
POST /preflightthe entire integration surface
request { "agent_id": "researcher", "task_ref": "job-142", "task_ceiling": 500, "estimated_units": 12 } approved { "approved": true, "task_ref": "job-142", "task_remaining_units": 488 } refused { "approved": false, "reason": "task_ceiling_exceeded", "task_ref": "job-142", "task_remaining_units": 8 }
Your code calls this, then calls your provider. Nothing of ours sits between the two.

The receipt

Every refusal is written down.

Each approved: false is persisted with the body the agent received, per agent and per task. A record that lands past a ceiling because preflight was skipped is kept as a leak, not hidden.

See the refusals →
Refusalswhat the agent got back
researcher job-8871 refused asked 40
Asked 40 units with the task at 492 of 500.
crawler nightly-crawl refused asked 200
Asked 200 units with the task at 1,840 of 2,000.
enricher batch-2211 refused asked 25
Asked 25 units with the task at 1,000 of 1,000.
Sample rows. Three agents, three tasks, one rule. "Asked" is units, not a dollar figure.

Keys

Keys you can revoke in one call.

Revoke, and the key is refused on its next request. Rotate, and the old key works for 24 hours, then revokes itself. Labels, expiry in days, 100 requests a minute per key, and an email when a key is used from a new address. Your provider keys never touch us.

See the keys view →
Keysrevoke, rotate, expire
production agb_demo0000… active
no expiry · last seen from 203.0.113.42
ci agb_demo1111… active
expires in 9d · last seen from 198.51.100.7
POST /keys/generateA new key, with an optional label and expiry in days.
POST /keys/rotateA new key now; the old one keeps working for 24 hours, then revokes itself.
POST /keys/revokeRevokes the calling key immediately, or another by its prefix.
Sample keys, the same ones the demo console lists. Neither authenticates anything.

Console

What the ceiling saved you from, as rows.

Calls refused and units refused over a window, the tasks burning down now, every customer by share of spend, and the one number that should be zero.

Open the sample console →
Console · overviewsample account, last 30 days
Refused · 30d
35
calls that never ran
Units refused · 30d
6,055
173 units per refused call
Live tasks · now
4
2 within a fifth of the ceiling
Refused, by day · 30d
Customers by share of spend · all time
cust_umbrella 9,310 units 54%
cust_acme 4,820 units 28%
cust_globex 2,140 units 12%
cust_initech 1,000 units 6%
1 call recorded past a ceiling, kept as a leak. The one number here that should be zero.
Sample account, the same rows the demo console shows. Refused is calls; units refused is what they asked for, not a dollar figure.

Pricing

Free to start. Cheap enough to leave on.

Every plan has every feature. The tiers differ in how many preflight calls a month they include, and in who answers when you write in.

free
$0forever
1,000preflight calls a month

Every feature. No card, no expiry.

Start free
builder
$29/ mo
50,000preflight calls a month

Every feature.

Get Builder
teamrecommended
$99/ mo
500,000preflight calls a month

Every feature. Priority support.

Get Team
scale
$299/ mo
2,000,000preflight calls a month

Every feature. Direct line to the founder.

Get Scale

What AgentBill does not do

Give one job a ceiling.

Free tier, no card, 1,000 preflight calls a month. If this page took longer to read than the integration takes, we did our job.

Get your API key →
pip install agentbill-sdk