How AI agents budget x402 API calls
A practical agent-first guide to enforcing spend policy before signing an x402 payment: validating 402 accepts[], comparing network, asset, payee, and amount against an allowlist, and recording debits only from successful payment receipts.
Before an autonomous agent signs any x402 payment, it should price-check the HTTP 402 `accepts[]` payload, compare every field against an internal allowlist and a per-task spending cap, and only sign when scheme, network, asset, payee, and amount all pass.
This article shows a concrete policy object and decision algorithm you can run inside an agent runtime before it attaches an `X-PAYMENT` header to a request. The examples use this marketplace, but the same logic applies to any x402 endpoint your agent might call.
This marketplace offers a global free tier of 3 successful calls total across all endpoints per IP + User-Agent identity. Successful free responses include an `X-Free-Calls-Remaining` header, but production budgeting must not rely on those calls being available; always plan for the paid path after the first request.
What the 402 challenge tells you
When the free tier is exhausted, an unpaid request to this marketplace returns HTTP 402 with a machine-readable `accepts[]` array. Each entry is a contract: sign this exactly, or do not sign at all.
| Field | Verified production value | Why it matters for budget |
|---|---|---|
| scheme | exact | Only exact-scheme pricing is accepted; no auctions or ranges. |
| network | base | Mainnet settlement on Base. Sepolia is only for sandbox testing. |
| asset | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 | USDC contract on Base mainnet. |
| payTo | 0x05e82e03753c7bc99fb24d10a876cce53f24b7b7 | The only approved merchant address. |
| maxAmountRequired | 5000 | Atomic USDC units; equals $0.005 per call. |
| resource | https://marketplaceforaiagents.com/api/public/v1/{slug} | Scope the signed authorization to this endpoint. |
Agents should never trust a hard-coded price. Always read the live requirement from `https://marketplaceforaiagents.com/.well-known/x402` or the 402 response itself before signing.
Build an allowlist policy object
Store policy as integer atomic units, never floating-point dollars. A task budget is a reservation, not a guaranteed debit, because a failed upstream call should not cost the agent anything.
const POLICY = {
// Only these settlement terms are permitted.
allowedSchemes: new Set(["exact"]),
allowedNetworks: new Set(["base"]),
allowedAssets: new Set([
"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", // Base USDC mainnet (lowercase)
]),
allowedPayees: new Set([
"0x05e82e03753c7bc99fb24d10a876cce53f24b7b7", // lowercase
]),
// Per-task cap in atomic USDC units (1 USDC = 1_000_000n).
taskBudgetAtomic: 50_000n, // $0.05 total for this task
maxPerCallAtomic: 5_000n, // $0.005 per call
maxCallsPerTask: 10,
// Track committed reservations and confirmed debits separately.
reservedAtomic: 0n,
spentAtomic: 0n,
};The policy also separates `reservedAtomic` from `spentAtomic`. A reservation is the amount you set aside when you plan to make a call; `spentAtomic` is updated only after a successful `X-PAYMENT-RESPONSE` receipt is decoded.
The budget algorithm before signing
Run this check on every `accepts[]` entry before producing an EIP-3009 signature. If any check fails, stop and surface a clear reason to the agent's planner.
function canAffordCall(policy, acceptsEntry, plannedCalls = 1) {
const {
scheme, network, asset, payTo, maxAmountRequired,
} = acceptsEntry;
const amount = BigInt(maxAmountRequired);
// 1. Structural validation
if (!policy.allowedSchemes.has(scheme)) return { ok: false, reason: "scheme_not_allowed" };
if (!policy.allowedNetworks.has(network)) return { ok: false, reason: "network_not_allowed" };
if (!policy.allowedAssets.has(asset.toLowerCase())) return { ok: false, reason: "asset_not_allowed" };
if (!policy.allowedPayees.has(payTo.toLowerCase())) return { ok: false, reason: "payee_not_allowed" };
// 2. Amount validation (integer atomic units only)
if (amount <= 0n) return { ok: false, reason: "amount_non_positive" };
if (amount > policy.maxPerCallAtomic) return { ok: false, reason: "per_call_over_cap" };
// 3. Budget reservation (estimate, not a debit)
const projectedSpend = policy.spentAtomic + (amount * BigInt(plannedCalls));
if (projectedSpend > policy.taskBudgetAtomic) {
return { ok: false, reason: "task_budget_exceeded" };
}
if (plannedCalls > policy.maxCallsPerTask) {
return { ok: false, reason: "call_count_over_cap" };
}
return { ok: true, amountAtomic: amount };
}This function returns the atomic amount only when every guard passes. The agent then reserves that amount, signs the authorization, attaches it as `X-PAYMENT`, and waits for the receipt before marking the spend as real.
When to stop and fall back
A disciplined agent treats the 402 as untrusted input. Any deviation from policy is a stop condition. Here is the decision matrix an agent runtime should implement.
| Situation | Decision | Fallback action |
|---|---|---|
| 402 has no accepts[] | Stop | Log malformed response; try another provider. |
| accepts[] has multiple entries | Evaluate each | Choose the first entry that matches the policy; stop if none match. |
| maxAmountRequired > per-call cap | Stop | Reject; do not sign a larger amount. |
| network/asset/payee mismatch | Stop | Do not sign; report policy violation. |
| scheme is not exact | Stop | Only exact pricing is supported here. |
| Upstream returns 5xx after payment | Hold reservation | Reconcile the receipt or on-chain transaction before recording or releasing it. |
| No X-PAYMENT-RESPONSE header | Hold reservation | Treat as unsettled; do not record spend or release the reservation. |
| Receipt decodes but tx status is unknown | Hold (optional agent policy) | Agent may apply its own confirmation rule before debiting or releasing. |
The key rule: a charge is only recorded when a successful paid response provides a base64 `X-PAYMENT-RESPONSE` that decodes to a transaction hash, network, and payer address. Until then, the amount remains a reservation.
Recording actual debits from receipts
After the agent sends `X-PAYMENT`, a successful response carries `X-PAYMENT-RESPONSE`. Decode it, extract the transaction hash, and only then move the reserved amount into `spentAtomic`.
async function recordPaidCall(policy, response, expectedAmountAtomic) {
const receiptHeader = response.headers.get("X-PAYMENT-RESPONSE");
if (!receiptHeader) {
return { settled: false, reason: "missing_receipt" };
}
const receipt = JSON.parse(atob(receiptHeader));
if (!receipt.transaction || !receipt.network) {
return { settled: false, reason: "incomplete_receipt" };
}
policy.spentAtomic += expectedAmountAtomic;
const newReserved = policy.reservedAtomic - expectedAmountAtomic;
policy.reservedAtomic = newReserved > 0n ? newReserved : 0n;
return {
settled: true,
transaction: receipt.transaction,
network: receipt.network,
payer: receipt.payer,
amountAtomic: expectedAmountAtomic,
};
}If the response is ambiguous — for example, a 5xx, a missing receipt, or a decoded receipt whose transaction status you cannot confirm — hold the reservation and reconcile the receipt or on-chain transaction before recording a debit or releasing the funds. Do not assume a failed HTTP response means no charge occurred, and do not assume an unconfirmed transaction is final unless your own policy defines that threshold.
Test the policy in Base Sepolia sandbox
Use the sandbox to exercise the budget algorithm without spending real USDC. Append `?network=base-sepolia` to any endpoint. The sandbox uses testnet USDC at `0x036CbD53842c5426634e7929541eC2318f3dCF7e` and returns mock data in the documented shape.
# 1. Discover current prices and endpoints
curl -s https://marketplaceforaiagents.com/.well-known/x402
# 2. Exhaust the free tier or skip straight to the paid path
curl -s "https://marketplaceforaiagents.com/api/public/v1/google-search?q=x402+protocol&network=base-sepolia" -v
# 3. When you see HTTP/1.1 402, inspect accepts[] and run your policy checks
# 4. Sign against testnet USDC, attach X-PAYMENT, and verify the mock receiptIn sandbox mode the 402 still returns `accepts[]`, but the amount is the same $0.005 in testnet USDC. This lets you validate your allowlist, budget algorithm, and receipt decoding before touching mainnet.
Where this marketplace fits
This marketplace hosts 38 live endpoints — search, news, images, commerce, maps, travel, jobs, academic, app stores, and video — all priced at $0.005 USDC per call on Base mainnet. There are no accounts, no API keys, and no monthly billing. Agents pay per call through x402.
- Browse human-readable listings at https://marketplaceforaiagents.com/marketplace
- Read the full discovery index at https://marketplaceforaiagents.com/llms.txt
- Read prose docs at https://marketplaceforaiagents.com/docs and the Markdown twin at https://marketplaceforaiagents.com/agent/docs.md
- Fetch the live catalog at https://marketplaceforaiagents.com/api/public/v1/catalog.json or the short path https://marketplaceforaiagents.com/catalog.json
- Inspect the OpenAPI spec at https://marketplaceforaiagents.com/api/public/v1/openapi.json
- See a concrete listing at https://marketplaceforaiagents.com/listings/google-search
Every agent should read live discovery before each task. Prices are uniform today, but hard-coding them is a future bug. Let the 402 response and `/.well-known/x402` be the source of truth, and let your policy object be the guardrail.
Next steps