Skip to main content

Buyer guide: pay for an API

:::tip Buyers need no account and no API key You are the buyer: you hold USDC and sign a payment. Seller API keys and credits belong to the seller side. There is nothing to register for before you can pay. :::

Gas is sponsored, so you never need XDC to pay for a call. You sign, and the facilitator submits the transaction.

From a terminal, or an agent with a shell

npx xdcai login # email code, an XDC wallet is created for you
npx xdcai wallet address # fund this with USDC
npx xdcai marketplace list --search weather,forecast
npx xdcai call https://provider.example/x402/endpoint

:::warning Fund on the XDC network only The same address exists on every EVM chain. USDC sent on Ethereum, Base or any other network does not arrive here and may be unrecoverable. Send USDC on XDC mainnet (chain id 50). :::

Every command prints one JSON object on stdout, with prompts and logs on stderr, so an agent can parse the result directly. Add --accept-terms when running non-interactively.

Useful flags:

npx xdcai marketplace list --search a,b --max-price 0.05 --sort most-used
npx xdcai call https://provider.example/endpoint --method POST --data '{"city":"Lisbon"}'
npx xdcai history # what you paid for, with transaction hashes

From Claude, ChatGPT, Cursor or another MCP client

{
"mcpServers": {
"xdcai": { "url": "https://api.xdcai.tech/mcp" }
}
}

That gives the assistant tools for the wallet, marketplace search, paid calls and history. Clients that cannot run an OAuth flow can point at https://api.xdcai.tech/mcp/open and log in from inside the conversation instead. Per-client setup is on the integrate page.

From your own code

A paid call is one request that comes back 402, and the same request repeated with a signed authorization attached.

import { createWalletClient, http, parseAbi } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { randomBytes } from "node:crypto";

// 1. Ask, and read the price out of the 402.
const first = await fetch(url);
const challenge = (await first.json()).accepts[0];

// 2. Sign an EIP-3009 authorization for EXACTLY the advertised amount.
const buyer = privateKeyToAccount(process.env.BUYER_KEY as `0x${string}`);
const authorization = {
from: buyer.address,
to: challenge.payTo,
value: challenge.amount ?? challenge.maxAmountRequired,
validAfter: "0",
validBefore: String(Math.floor(Date.now() / 1000) + challenge.maxTimeoutSeconds),
nonce: `0x${randomBytes(32).toString("hex")}`,
};

const signature = await buyer.signTypedData({
domain: {
name: "USDC",
version: "2",
chainId: 50,
verifyingContract: "0xfA2958CB79b0491CC627c1557F441eF849Ca8eb1",
},
types: {
TransferWithAuthorization: [
{ name: "from", type: "address" },
{ name: "to", type: "address" },
{ name: "value", type: "uint256" },
{ name: "validAfter", type: "uint256" },
{ name: "validBefore", type: "uint256" },
{ name: "nonce", type: "bytes32" },
],
},
primaryType: "TransferWithAuthorization",
message: authorization,
});

// 3. Repeat the request with the payment attached.
const payload = {
x402Version: 2,
scheme: "exact",
network: "eip155:50",
payload: { signature, authorization },
};

const paid = await fetch(url, {
headers: { "PAYMENT-SIGNATURE": Buffer.from(JSON.stringify(payload)).toString("base64") },
});

Three things to get right:

  • Pay the exact amount. The exact scheme rejects an authorization signed for a different value, so do not round or add a tip.
  • Use the right header. v2 sellers read PAYMENT-SIGNATURE, v1 sellers read X-PAYMENT. The accepts[] entry tells you which: a v2 entry has amount and a CAIP-2 network like eip155:50, a v1 entry has maxAmountRequired and network: "xdc".
  • The EIP-712 name is USDC. Not USD Coin. The wrong name changes the domain separator and the signature will not recover.

Getting your receipt

A successful paid call returns a receipt header, PAYMENT-RESPONSE on v2 or X-PAYMENT-RESPONSE on v1, holding the settlement result including the transaction hash. If the seller omits it, the payment can still be found on-chain by the nonce you signed: the USDC contract emits AuthorizationUsed(authorizer, nonce).

npx xdcai history verify --tx 0x...