Get API key →

How to cap what one agent run can spend

PythonNode.js

A provider spend cap is bound to a project, or to an organization over a calendar month. A single 3-hour research loop is neither. AgentBill's ceiling is bound to a task_ref, consulted before each call, on units you define.

What a monthly cap is bound to

Provider spend caps are real and they fire. What they are bound to is a project, or an organization over a calendar month. So either the run is too small to move a monthly number, or the number low enough to catch it takes every agent in the organization down with it until the 1st. AgentBill's ceiling is bound to one task instead: if the units already used plus this call's estimate would cross it, preflight answers approved: false and the SDK raises before your provider call goes out.

Install

pip install agentbill-sdk

Give the run an id, and give that id a ceiling

Two parameters do the work. task_ref is your name for this run, and every call that passes it is checked against the same ceiling. task_ceiling is that ceiling, in units you define. The preflight that names a new run opens it, or the console does before the run starts; once the run exists a value from code is not applied, so a retry cannot raise the ceiling it was meant to respect, and the console's last save is the one in force.

from agentbill import AgentBillClient

client = AgentBillClient(api_key="agb_your_key")

# 1 unit = 1 cent here, so this run has $5 across every call,
# tool and retry that passes the same task_ref.
client.preflight(
    agent_id="researcher",     # a label for attribution, not a budget
    task_ref="job-142",        # your name for this run
    task_ceiling=500,          # the ceiling for the whole run
    estimated_units=12,        # what this one call is worth
)

# a refused call raised TaskCeilingExceededError above; nothing to check here
result = run_agent()

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

Every later call in the same run passes task_ref and nothing else about the budget. It does not need to know the ceiling, or what the calls before it spent.

# A different agent, a different tool, the same run and the same ceiling.
client.preflight(agent_id="writer", task_ref="job-142", estimated_units=40)

Use the @gate decorator (shortest path)

The @client.gate() decorator does the preflight before the body and the record after it, and it takes the same two parameters. On an exception it settles with success=False, which releases the reservation instead of billing it.

@client.gate(agent_id="researcher", task_ref="job-142",
             task_ceiling=500, estimated_units=12)
def run_agent(task: str) -> str:
    # preflight runs before this body, record runs after it
    return do_the_work(task)

Handle the refusal

The exception carries the numbers, so the handler can say what happened without a second call.

from agentbill import TaskCeilingExceededError

try:
    result = run_agent("analyze this")
except TaskCeilingExceededError as e:
    return {"error": f"run {e.task_ref} hit its ceiling of {e.task_ceiling} units"}

What a per-request ceiling is, and is not

There is a second, narrower ceiling: ceiling on the client refuses any single call whose estimated_units exceed it. It is a sanity check on one bad estimate. It is not the cross-call ceiling above, and on its own it lets through a loop of two hundred individually reasonable calls.

# No single call may cost more than 50 units. The run still needs a task_ceiling.
client = AgentBillClient(api_key="agb_your_key", ceiling=50)

And note what is not on this list. agent_id is a label the console groups tasks and refusals by; it carries no budget of its own. Ceilings are bound to a run, a customer, or a single call, never to an agent name.

Node.js

import { preflight, record } from 'agentbill'  // reads AGENTBILL_API_KEY

// The run has 500 units across every call that passes job-142.
// A refused call throws, so your catch clause decides what happens next.
await preflight({ agentId: 'researcher', taskRef: 'job-142',
                  taskCeiling: 500, estimatedUnits: 12 })

const result = await runAgent()

await record({ agentId: 'researcher', taskRef: 'job-142', units: 12 })

Get API key →