Verified against @dexterai/x402 5.3.1 · 2026-07-07
Budgets & spend caps
By the end of this page you will have a budget account: a payment-aware fetch that stops an agent from spending past a total limit, a per-request cap, an hourly cap, and a domain allowlist you set.
What a budget account is
createBudgetAccount wraps the x402 client with spending controls. It returns an object whose fetch behaves like normal fetch, except it pays x402 challenges automatically and refuses to pay past your limits. Every payment is recorded in an in-memory ledger you can read back.
The controls are:
| Control | Config field | Enforces |
|---|---|---|
| Total budget | budget.total | Cumulative spend across the account's whole life |
| Per-request cap | budget.perRequest | The most a single request may pay (optional) |
| Hourly cap | budget.perHour | The most spent in any rolling hour (optional) |
| Domain allowlist | allowedDomains | Only these hosts may be paid; if omitted, any host may be paid |
Amounts are USD strings, for example '50.00'.
Working example
walletPrivateKey is a Solana key — a base58 string or a 64-byte JSON array. For Base/EVM payments, pass evmPrivateKey instead (a hex key; requires viem installed). The config also inherits the wrapFetch options — facilitatorUrl, preferredNetwork, rpcUrls, maxAmountAtomic, accessPass — so a budget account is a superset of a plain payment fetch.
The wallet must already hold funds. See Pay per call — Node agent for funding a keypair wallet.
Reading spend back
The account exposes live counters:
| Property | Type | What it holds |
|---|---|---|
spent | string | Total spent, formatted ('$12.34') |
remaining | string | Budget left, formatted ('$37.66') |
payments | number | Count of payments made |
spentAmount | number | Total spent as a raw number |
remainingAmount | number | Budget left as a raw number |
hourlySpend | number | Spent in the last rolling hour |
ledger | readonly PaymentRecord[] | Every payment: amount, domain, network, timestamp |
reset() | () => void | Clears all spend history and counters |
What happens at a cap
When a request would cross a cap, the wrapped fetch throws before any payment is signed — it does not silently under-deliver or partially pay. The thrown value is an X402Error with a code you can branch on:
amount_exceeds_max— the total, per-request, or hourly cap would be crossed.payment_rejected— the request host is not inallowedDomains.
The ledger is in-memory and per-process. It counts only payments this account instance made; it is not shared across processes and does not survive a restart or reset(). A budget account bounds spend within one running agent, not across a fleet.