What overspending looks like
An agent overspends when it pays more than you intended, and it almost never happens as one big deliberate payment. It happens as drift: a loop that pays a little too many times, a retry that re-pays for the same thing, a price that crept up while no one was watching. By the time you notice the bill, the cause is usually a pattern, not a single event.
So preventing overspend is two jobs. The first is a hard backstop that bounds the worst case no matter what: a wallet-enforced limit the agent cannot exceed. The second is handling the common causes so you rarely hit that backstop at all. This page covers both. For the full limit mechanics see how-to-set-up-agent-spending-limits, and for the broader security picture see how-to-secure-ai-agent-payments. The spending controls product page is the reference.
The backstop: a wallet-enforced cap
Start with the control that makes everything else optional rather than critical. A per-transaction cap and a per-period allowance, set in the dashboard, are enforced by the wallet before settlement, and the agent cannot change them. Read them back to confirm:
const res = await fetch(
`https://api.blockchain0x.com/v1/agents/${agentId}/spend-permissions`,
{ headers: { Authorization: `Bearer ${process.env.B0X_API_KEY!}` } },
);
const permissions = await res.json(); // per_tx_wei, allowance_wei, period_secondsSet per_tx_wei to the largest single payment that is ever sensible, so a runaway can only spend that much per call, and allowance_wei to the total you are willing to lose in a period if everything goes wrong. With those in place, the worst overspend is bounded to a number you chose, and the rest of this page is about not hitting it.
Cause 1: runaway loops and retries
The most common cause by far. An agent gets a failure, retries the same paid call, fails again, and loops, spending each time. Or its planner gets stuck repeating a step. The cap stops the bleed, but the fix is to not loop.
When a paid call fails, do not retry it unchanged. Treat the failure as a signal and let the planner choose a different action or stop:
const res = await fetchWithPay(url);
if (!res.ok) {
// refused (limit), insufficient funds, or upstream error - do NOT retry as-is
return { ok: false, status: res.status };
}Tell the planner in its instructions that a refused or failed payment means stop or change approach, never retry identically. An agent that retries a refused payment turns one blocked call into a tight spend loop that hammers the cap, which is both wasteful and the thing that looks like an attack in your logs.
Cause 2: duplicate work
The quieter cause. The agent pays for the same result twice because it asked the same question twice, or restarted and redid completed work. Each duplicate is a real payment for nothing.
Cache results keyed by the request, so a repeat is a cache hit rather than a second payment:
const seen = new Map();
async function paidOnce(key, doPaidCall) {
if (seen.has(key)) return seen.get(key);
const result = await doPaidCall();
seen.set(key, result);
return result;
}Match the cache lifetime to how fresh the data must be. This is an ordinary application cache, not a platform feature, and it is the cheapest spend you will ever cut: the payment you simply do not make because you already have the answer.
Cause 3: price drift
The slow cause. An endpoint your agent pays raised its price, or you priced your own routes too low and usage grew, and spend crept up without any single event to notice. Drift does not trip a per-call cap because each call is individually fine; it shows up only in the total.
The period allowance is your guard here: even if per-call prices drift up, the period allowance_wei stops the total at the number you set. Beyond that, review actual spend periodically against expectations, and treat a quote that exceeds your per-transaction cap as a signal that a price changed, since the wallet will refuse it and surface the change rather than quietly paying more.
Alert on spend rate
Bounds stop disasters; alerts catch them early. Watch spend rate, not just cumulative total, because a runaway shows up as a rate spike long before it exhausts the allowance. Track payments per minute and spend per period from the payment.sent webhook stream, and alert when the rate jumps or the period spend crosses, say, 70 percent of the allowance.
An early alert lets a human or an automated guard stop the agent before it spends the whole allowance repeatedly. The cap guarantees you cannot lose more than the allowance in a period; the alert is how you avoid losing even that to a loop you could have stopped in the first minute.
A worked budget
Concrete numbers make the bound tangible. Say your agent normally makes 200 paid calls a day at about half a cent each, so it spends roughly a dollar, and its priciest single legitimate call is five cents. Set per_tx_wei to 200000 ($0.20, four times the largest real call) and allowance_wei to 5000000 ($5.00, five times a normal day) on a daily period_seconds of 86400.
Now trace a runaway: a loop starts re-paying a half-cent call as fast as it can. Each payment is under the per-transaction cap, so the cap alone does not stop it, but after roughly a thousand calls the daily allowance is exhausted and every further payment is refused. Your worst-case loss for the day is five dollars, not five thousand, and your spend-rate alert fires within the first minute when the rate jumps from a trickle to a flood. The cap shaped the loss; the allowance bounded it; the alert caught it. That is the whole defense working together, and the only number you risked is the one you chose.
Test the runaway scenario
Do not wait for a real runaway to discover whether your defenses work. Induce one on Base Sepolia. Write a throwaway loop that makes the same paid call repeatedly with a sk_test_ agent whose allowance_wei you have set low, and watch three things happen: payments settle until the allowance is reached, then every call comes back refused with a non-2xx, and your spend-rate alert fires on the spike. Confirm your agent's failure handling treats the refusals as a stop rather than retrying into them.
This test is cheap on testnet and tells you something you cannot learn any other way: that when a loop really happens in production, the money stops where you said it would and you find out fast. Run it once when you set the limits, and again whenever you change them.
Common pitfalls
Three traps.
Relying on the prompt to cap spend. A prompt budget is not enforced. The wallet cap is. Use the prompt to discourage retries, not to stop overspend.
Retrying refused payments. This is the single biggest cause of runaway spend. Make a refused payment a stop, not a retry.
Watching only the total. By the time the total looks wrong, the loop has been running a while. Watch the rate and alert on the spike.
What to ship today
Set a per-transaction cap and a period allowance, make refused payments stop rather than retry, cache repeated calls, and alert on spend rate from the payment.sent stream. That combination bounds the worst case and catches the common one early. For the full limit mechanics see how-to-set-up-agent-spending-limits. Pricing is on the pricing page.