Disclosure: this post was written with AI assistance and reviewed before publishing. I sell a starter kit based on this code (link at the end); everything below works without buying it.
HTTP has had a status code for this since 1997: 402 Payment Required. With the x402 protocol it finally does something. An AI agent calls your endpoint, gets a 402 with machine-readable payment terms, signs a USDC authorization and retries. Then it gets the data. There's no signup, no API key and no Stripe account.
I wanted to know how little it takes, so I shipped a real one: freetier-agent-api.pages.dev. It's one _worker.js on Cloudflare Pages (free plan) with no npm dependencies.
GET /api/apis/search?q=weather with no payment returns PAYMENT-REQUIRED header (x402 v2) and a JSON body (x402 v1, for older clients). Both say: pay 5000 atomic units of USDC (= $0.005) on Base to this address.transferWithAuthorization and retries with a PAYMENT-SIGNATURE header./verify, runs the handler, then calls /settle. The facilitator broadcasts the transfer and pays the gas, and the USDC lands in my wallet.
Coinbase's facilitator needs CDP keys on mainnet. Several public facilitators don't: PayAI (facilitator.payai.network) and xpay ( facilitator.xpay.sh) both settle Base mainnet USDC without auth. I use PayAI and fall back to xpay:
const FACILITATORS = ['https://facilitator.payai.network', 'https://facilitator.xpay.sh'];
async function fac(op, body) {
for (const f of FACILITATORS) {
try {
const r = await fetch(`${f}/${op}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
const j = await r.json();
if ('isValid' in j || 'success' in j) return j;
} catch {}
}
return { error: 'no facilitator' };
}
Verify, run, settle, in that order. If the handler fails (bad URL, missing parameter), return 400 and never call settle, so the agent pays nothing. This matters, because agents retry a lot.
I generated a brand-new empty wallet with viem, signed a correct authorization and sent it to the live API. The facilitator answered invalid_exact_evm_insufficient_balance. So the signature, EIP-712 domain ( USD Coin, version 2, chain 8453), amount and recipient were all accepted, and the only thing missing was money. That's a nice zero-cost end-to-end check.
A paid API nobody can find earns nothing. What worked:
extensions.bazaar with input/output JSON schemas)/openapi.json with x-payment-info on each paid operation, plus /.well-known/x402
POST https://api.x402dash.com/v1/register {"url": ...} per endpoint. It probes the 402 live@x402/extensions handles/api/apis/search ($0.001): 2,230 link-checked public and free-tier APIs, with filters like auth=none&alive=yes
/api/free-tiers ($0.001): current free-tier limits for dev services and LLM APIs/api/robots-check ($0.001): may user-agent X crawl this URL?/api/page-meta ($0.001): title/OG/headings of a public page (respects robots.txt)
Free: /api/apis/sample, /openapi.json, /health.
If you want the cleaned-up code with tests and the listing steps, it's in the x402 Pay-Per-Call Starter. Or just read the 402 response from the live API and build your own.