◆ Autopilot Studio
DevNotes

How an AI Agent Pays an x402 API: a 20-Line Client in JS

2026-10-02 · 4 min read

a small friendly robot holding a glowing coin next to a vending machine made of server racks, dark blue and teal palette, minimal isometric

An AI agent that can pay for things on its own can use any x402 API it discovers, with no sign-up and no API key. The client side is shorter than most people expect. Below is the full thing, followed by the parts that matter in practice: reading the price first, getting a receipt, capping the spend and testing without money.

The 20-line client

Install npm i @x402/fetch @x402/evm viem. You need a wallet that holds a little USDC on Base. It does not need ETH.

import { x402Client, wrapFetchWithPayment, decodePaymentResponseHeader } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";

const signer = privateKeyToAccount(process.env.AGENT_PRIVATE_KEY);   // a dedicated wallet with a few dollars, never your main one
const client = new x402Client()
  .register("eip155:*", new ExactEvmScheme(signer))
  .setSpendControls({ maxAmountPerPayment: "$0.05" });               // hard cap per call

const pay = wrapFetchWithPayment(fetch, client);

const res = await pay("https://autopilot-studio.kariguetoazul.workers.dev/v1/embed", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ texts: ["hello world", "olá mundo"] }),
});

console.log(res.status, await res.json());   // { model: "bge-m3", dimensions: 1024, count: 2, embeddings: [[...], [...]] }
const receipt = decodePaymentResponseHeader(res.headers.get("payment-response"));
console.log(receipt);                        // { success: true, payer: "0x...", transaction: "0x...", network: "eip155:8453" }

pay behaves like fetch. If the server answers 402, it reads the challenge, signs an authorization with your key, repeats the request with a PAYMENT-SIGNATURE header and returns the final response. The private key never leaves your process: it only signs.

Look before you pay

Every x402 server tells you the price in the 402 itself, and good ones also publish a free price list. For the service above, GET /v1/pricing lists every endpoint with its price and body, and the challenge is one curl away:

curl -si -X POST https://autopilot-studio.kariguetoazul.workers.dev/v1/embed \
  -H 'content-type: application/json' -d '{"texts":["hi"]}' \
  | grep -i '^payment-required:' | cut -d' ' -f2 | tr -d '\r' | base64 -d

The decoded JSON has accepts[0].amount in USDC base units (six decimals, so 1000 is $0.001), the payTo address and the token contract. An agent can run exactly this check before it commits to a call.

Cap what your agent can spend

An autonomous agent with a wallet needs a budget. @x402/fetch ships with a spend control that defaults to $1 per payment. We tested it against a fake server: a $1.00 challenge was paid, a $5.00 one was refused with All payment requirements were rejected by spendControls.maxAmountPerPayment. You can tighten it as in the code above ("$0.05" accepted a $0.05 challenge and refused $0.99), or turn controls off with spendControls: false, which you should not do for an unattended agent.

Three more habits that cost nothing:

  • Fund the agent wallet with a few dollars, not your savings. Top it up on a schedule.
  • Keep the key in an environment variable or a secret store, never in the repository or the prompt.
  • Log every receipt. The transaction hash lets you audit each payment on a Base block explorer.

Errors cost nothing, successes cost every time

A correct server settles only after it produced a successful response. So a malformed request (400) or a server hiccup (5xx) is free, and your wrapper can retry those. A successful call is charged each time, which means a loop that calls the same paid endpoint "just to be sure" burns money. Make the paid call idempotent in your own code: cache the result by request hash.

Test it without any money

Point the client at a throwaway key. With a freshly generated wallet that holds no USDC, the facilitator answers invalid_exact_evm_insufficient_balance. That is a good result: it proves the endpoint's challenge, your client's signature and the facilitator's checks all line up, and the only thing missing is funds. For a full end-to-end run with receipts, run a mock facilitator locally and set the server's facilitator URL to it. The server side of that setup is in How to Add x402 Payments to a Cloudflare Worker with Hono.

Things you can call right now

The service used in these examples has eight endpoints you can pay for with this exact client: embeddings ($0.001), summaries ($0.004), translation ($0.003), prompt writing ($0.005), speech synthesis ($0.01), speech transcription ($0.01), images ($0.02) and a weekly prompt pack ($0.99). The API page has the request bodies and a copy-paste example.

FAQ

Does my agent need ETH to pay?

No. The agent only signs an EIP-3009 authorization. The facilitator submits the USDC transfer on Base and pays the gas, so the wallet only needs USDC.

How do I stop an agent from overspending?

Set a per-payment cap on the client. In @x402/fetch the default cap is $1 per payment, and you can tighten it with client.setSpendControls({ maxAmountPerPayment: "$0.05" }).

Am I charged when the API returns an error?

No. A well-built x402 server only settles the payment when its response status is below 400, so 400 and 5xx responses are free. A successful call is charged every time, so do not retry successful calls blindly.

Which network and token does this use?

USDC on Base mainnet (eip155:8453) in our examples. The same client can handle other EVM networks you register.

#x402#ai agents#javascript#usdc#payments

Found this useful? Tip the studio in crypto

Every EVM chain works. USDC on Base is recommended: fees are a fraction of a cent. No account needed — it goes straight to the creator's wallet.

0x13dd72Fa0E7504790585D92bD98c720f6fD2aBa6

More from DevNotes