How AI agents pay for APIs: wallets, signatures and facilitators
By Requestway · · 7 min read
How AI agents pay for APIs comes down to three pieces: a wallet the agent controls, a signature that authorises one specific payment, and a facilitator that puts that payment on chain. The x402 protocol ties them to ordinary HTTP, so an agent can discover a price, pay it and get the response in a single retry, without an account or an API key. This guide follows one payment from the first request to the receipt and explains what each party can and cannot do.
The whole exchange in six steps
- The agent requests a resource, exactly as it would any other URL.
- The server answers
402 Payment Requiredwith aPAYMENT-REQUIREDheader describing what it accepts. - The agent picks an option, signs a payment for that amount with its wallet, and retries the request with a
PAYMENT-SIGNATUREheader. - The server checks the payment locally and asks a facilitator to verify it (
/verify). - The server runs its handler, then asks the facilitator to settle (
/settle), which submits the transfer on chain. - The server responds
200with the resource and aPAYMENT-RESPONSEheader containing the transaction hash.
From the agent's point of view, steps 4 and 5 are invisible: it sent one request, got a 402, sent a second request and got the data. The x402 overview has the header details; the rest of this guide covers the moving parts.
The 402 tells the agent what to pay
The PAYMENT-REQUIRED header is base64-encoded JSON. Decoded, the important part is the accepts list. Each entry is one way to pay:
{
"x402Version": 2,
"accepts": [
{
"scheme": "exact",
"network": "eip155:8453",
"amount": "10000",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"payTo": "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
"maxTimeoutSeconds": 60,
"extra": { "name": "USD Coin", "version": "2" }
}
]
}
This example is trimmed to the fields the agent needs:
scheme: how to pay.exactmeans one transfer of exactly this amount.network: a CAIP-2 chain id.eip155:8453is Base;eip155:84532is the Base Sepolia testnet.amount: a string in atomic units. USDC has 6 decimals, so10000is $0.01.asset: the token contract, here USDC on Base.payTo: the address that receives the money.maxTimeoutSeconds: how long the payment may take.extra: the token's EIP-712nameandversion, which the agent needs to sign correctly.
A server may list several entries, for example the same price on two networks, and the agent pays on whichever one it holds funds on. Older version 1 servers put the requirements in the 402 body and expect the payment in X-PAYMENT, so you will still see both versions.
Agents can also find prices before calling. The Laravel package serves GET /.well-known/x402, a list of every paid route with its accepts entries, in the shape of the x402 discovery ("Bazaar") listing.
The wallet
The agent needs a wallet: a private key and the address derived from it, holding USDC on a network the API accepts. That is all. It does not need ETH for gas, because for EIP-3009 transfers the facilitator submits the transaction and pays gas.
The wallet is the agent's spending limit. An agent with a bug, a bad prompt or a compromised host can only spend what the wallet holds, so the standard advice is a dedicated, low-balance wallet per agent, topped up as needed, never your main treasury. The key belongs in the agent's environment, not in code.
For testing, use Base Sepolia: test USDC comes from https://faucet.circle.com (choose USDC and Base Sepolia), and transactions show up on sepolia.basescan.org. The Base Sepolia testing guide covers the setup.
The signature
For the exact scheme on EVM chains, the agent does not send a transaction. It signs an EIP-3009 transferWithAuthorization message using EIP-712 typed data. The signed message says, in effect: "move this many units of this token from my address to this address, valid between these two times, identified by this nonce".
The fields are:
| Field | What it fixes |
|---|---|
from |
the agent's address |
to |
the API's payTo |
value |
the amount, in atomic units |
validAfter, validBefore |
the window in which it can be executed |
nonce |
a unique value; the token contract refuses to execute the same authorization twice |
The EIP-712 domain ties the signature to one token contract on one chain, using the name and version from extra. Change any field and the signature no longer matches.
This is the property that makes the whole system safe to automate. The signature authorises one transfer of one amount to one address within one time window. It is not an allowance, not a standing permission and not a key. If someone intercepts it, the most they can do is execute that exact payment to that exact recipient, once.
The agent then base64-encodes the payment payload, puts it in PAYMENT-SIGNATURE and retries the original request.
The facilitator
A facilitator is a service exposing three endpoints: /verify, /settle and /supported. The API server, not the agent, talks to it.
/verifychecks the signature and the payer's balance without moving money./settlesubmits thetransferWithAuthorizationon chain and pays the gas./supportedlists the schemes and networks it handles.
What a facilitator cannot do matters as much. It cannot change the amount or the recipient, because both are inside what the payer signed. It never needs to hold the funds: the token moves directly from the agent's wallet to payTo. And the API server never needs a private key either; it only needs an address to be paid at.
There are a few options. https://x402.org/facilitator is free, needs no account and works on testnets only. Coinbase Developer Platform runs a managed mainnet facilitator at https://api.cdp.coinbase.com/platform/v2/x402 that requires authenticated, per-request signed headers. You can also self-host any service that exposes the three endpoints.
Before calling the facilitator, a well-behaved server also checks things locally. The Laravel package, for example, checks amount, asset, payTo, network, resource and validity window, and claims each payment's fingerprint in a cache before verification, so a replayed payment is rejected without a network call.
The receipt
After settlement the server responds 200 with the resource and a PAYMENT-RESPONSE header: base64 JSON with success, transaction (the on-chain hash), payer and the network. The agent can log the transaction hash and anyone can look it up on a block explorer such as basescan.org. That is the agent's proof of payment and the API's proof of revenue, without either side trusting the other's records.
Depending on the server's settlement mode, the transfer is settled before the response is sent (the safe default in the Laravel package) or just after it (faster, with a small risk the server has to absorb).
What the client code looks like
With the official @x402/fetch package, the 402, signing and retry all happen inside a wrapped fetch:
import { wrapFetchWithPaymentFromConfig, decodePaymentResponseHeader } from '@x402/fetch'
import { ExactEvmScheme } from '@x402/evm'
import { privateKeyToAccount } from 'viem/accounts'
const account = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`)
const fetchWithPayment = wrapFetchWithPaymentFromConfig(fetch, {
schemes: [{ network: 'eip155:84532', client: new ExactEvmScheme(account) }],
})
const response = await fetchWithPayment('https://api.example.com/premium-data')
const receipt = response.headers.get('PAYMENT-RESPONSE')
if (receipt) console.log(decodePaymentResponseHeader(receipt))
Use network: 'eip155:*' to support all EVM chains. A rejected payment comes back as another 402, not an exception, so check response.status before using the body.
If you want the agent to look at the price before agreeing to pay, read the header from a plain request first. It is ordinary base64 JSON:
const probe = await fetch('https://api.example.com/premium-data')
if (probe.status === 402) {
const header = probe.headers.get('PAYMENT-REQUIRED')
const required = JSON.parse(Buffer.from(header ?? '', 'base64').toString('utf8'))
const cheapest = Math.min(...required.accepts.map((a: { amount: string }) => Number(a.amount)))
if (cheapest > 50000) throw new Error('Over budget: more than $0.05 for one request')
}
Here 50000 atomic units is an example budget of $0.05. Number is fine for a comparison at this scale; keep amounts as strings anywhere you store or forward them.
Who is protected from what
| Risk | What prevents it |
|---|---|
| The server charges more than advertised | The agent signs a fixed value; nobody can raise it |
| The facilitator redirects the money | to is inside the signature |
| A payment is reused for a second request | Unique nonce; the token contract rejects a second execution, and servers keep a replay cache |
| A cheap payment is presented at an expensive endpoint | Servers check the amount against the route's price; the Laravel package also binds the payment to the resource URL |
| A stale payment is executed much later | validBefore |
| The agent's key leaks | Losses are capped by the wallet balance, which is why it should be small |
| The agent pays and the server fails to deliver | In settle-before-response mode, a handler that fails is never settled, so it costs the agent nothing |
The remaining risk sits with the agent's operator: an agent that decides to pay for things it should not. Budgets, small wallets and price checks like the one above are the controls.
Building the API side
If you are on the other side of this exchange, the Laravel guide and the Express, Next.js and Hono guide show how to return the 402 and accept payments in a few lines. To see the exchange from the agent's side without writing a client, Requestway's endpoint tester calls your route, reads the 402, pays it with test USDC on a testnet and shows each step, including the receipt and the explorer link.
FAQ
Does an AI agent need ETH to pay for an x402 API?
No. With the exact scheme on EVM chains, the agent signs an EIP-3009 authorization and the facilitator submits it and pays the gas. The agent only needs USDC on a network the API accepts.
Can an API take more money than the agent agreed to pay?
No. The amount and recipient are inside the signed authorization, and it can be executed only once. Neither the server nor the facilitator can change them.
How does an AI agent know what an API costs before paying?
The 402 response lists the price in its PAYMENT-REQUIRED header, and the agent can decode it before signing anything. Some servers also publish their paid routes and prices at /.well-known/x402.
Should an AI agent use my main crypto wallet?
No. Give each agent a dedicated wallet with a small balance and top it up as needed. The balance is the agent's hard spending limit if anything goes wrong.
Related articles
-
Concepts
What HTTP 402 Payment Required means and how x402 uses it
8 min read
-
Concepts
What x402 facilitators do and how to choose one
8 min read
-
Strategy
x402 vs API keys and subscriptions: when pay-per-request makes sense
6 min read