10-minute walkthrough
Make your first paid call in 10 minutes.
From zero to a successful paid HTTP request, settled in USDC on Base mainnet. Total cost: less than one penny.
What you need
- A machine with Node 18+ or Bun
- ~$0.10 USDC on Base mainnet (less than 30 cents on a CEX after fees)
- About 10 minutes
-
Step 1 - install the SDK (1 min)
# Bun + viem + Coinbase x402 client SDK mkdir my-x402-agent && cd my-x402-agent bun init -y bun add viem @x402/core @x402/evm
-
Step 2 - create a wallet (1 min)
// generate a fresh wallet (or import your own from MetaMask, etc.) import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; const pk = generatePrivateKey(); // 0x... 64 hex chars const account = privateKeyToAccount(pk); console.log("Address:", account.address); console.log("Save this private key somewhere safe and never commit it.");If you already have a Base wallet, skip this. Just export the private key.
-
Step 3 - fund the wallet with USDC on Base (3-5 min)
# 1. Buy USDC on Base on a CEX (Coinbase, Binance, etc.) and withdraw # to YOUR_ADDRESS on the Base network. ~30 seconds, ~$0.01 gas. # 2. Or bridge from Ethereum mainnet via https://superbridge.app/base # 3. Verify the balance once it lands: curl -s 'https://api.basescan.org/api?module=account&action=tokenbalance' \ '&contractaddress=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' \ '&address=YOUR_ADDRESS&tag=latest&apikey=YourApiKeyToken' # Or one-liner using viem: node -e " const { createPublicClient, http, formatUnits } = require('viem'); const { base } = require('viem/chains'); const c = createPublicClient({ chain: base, transport: http() }); c.readContract({ address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', abi: [{ name: 'balanceOf', type: 'function', stateMutability: 'view', inputs:[{type:'address'}], outputs:[{type:'uint256'}] }], functionName: 'balanceOf', args: ['YOUR_ADDRESS'] }).then(b => console.log(formatUnits(b, 6), 'USDC')); " # You only need ~$0.10 USDC to make tens of paid calls. -
Step 4 - poke the network for free (30s)
# Even before funding, you can verify the network is live and reachable. # Discovery is free. curl -s https://x402.adametherzlab.com/.well-known/x402-services.json | head -40 # Look for: "total": 45, "verticals": [...], "services": [...]
-
Step 5 - get the 402 challenge (30s)
# Cheapest endpoint in the network: $0.002 USDC for a single page scrape. curl -i 'https://x402.adametherzlab.com/api/scrape?url=https://example.com' # You should see: # HTTP/1.1 402 Payment Required # x-payment-required: <base64-encoded payment requirements> # That base64 blob, decoded, looks roughly like: # { # "x402Version": 1, # "accepts": [{ # "scheme": "exact", # "network": "eip155:8453", # "amount": "2000", # "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", # "payTo": "0x24A93e03deD53ac77d06611c763d2e3B4C6aaF40", # "resource": "https://x402.adametherzlab.com/api/scrape" # }] # } # # That's the payment challenge. Sign it next. -
Step 6 - sign + resubmit (2 min)
// first-paid-call.ts -- under 30 lines. import { createWalletClient, http, publicActions } from "viem"; import { privateKeyToAccount } from "viem/accounts"; import { base } from "viem/chains"; import { x402Client, x402HTTPClient } from "@x402/core/client"; import { ExactEvmScheme, toClientEvmSigner } from "@x402/evm"; const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`); const wallet = createWalletClient({ account, chain: base, transport: http() }).extend(publicActions); const signer = toClientEvmSigner(account, wallet); const scheme = new ExactEvmScheme(signer); const core = new x402Client(); core.register("eip155:8453", scheme); const http402 = new x402HTTPClient(core); const URL = "https://x402.adametherzlab.com/api/scrape?url=https://example.com"; const r1 = await fetch(URL); // 402 challenge const challenge = http402.getPaymentRequiredResponse(n => r1.headers.get(n), await r1.json()); const payload = await http402.createPaymentPayload(challenge); const headers = http402.encodePaymentSignatureHeader(payload); const r2 = await fetch(URL, { headers }); // resubmit with X-Payment header console.log("status:", r2.status); // 200 console.log("body:", await r2.json()); // { content: "...", status_code: 200 } console.log("settle:", r2.headers.get("X-PAYMENT-RESPONSE"));Run with
PRIVATE_KEY=0x... bun first-paid-call.ts -
Step 7 - read the response (1 min)
# Decoded X-PAYMENT-RESPONSE looks like: { "success": true, "transactionHash": "0xabc123...", // verifiable on basescan.org/tx/<hash> "network": "eip155:8453", // Base mainnet "payer": "0xYourAddress", "amount": "2000" // 2000 = 0.002 USDC (6 decimals) } # That hash IS your receipt. The funds are already settled on Base. # No webhooks, no idempotency keys, no async invoice flow.
Where to go next
- Browse all 45 endpoints by vertical: homepage
- See three live cached responses on the /showcase
- Read the canonical x402 spec: github.com/coinbase/x402
- Listed on Coinbase Bazaar: bazaar.cdp.coinbase.com
Need an endpoint that doesn't exist? Open an issue on the AdametherzLab org or just curl one of the existing ones and tell us what you wish it returned.