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
  1. 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
  2. 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.

  3. 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.
  4. 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": [...]
  5. 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.
  6. 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

  7. 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

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.