API
x402: pay per call
No account, no key: an agent with a wallet pays each call in USDC, and a call that fails costs nothing.
What it is
x402 is an open standard for paying an API per request over HTTP, with HTTP's own 402 Payment Required. Call a paid route with no key and Iris answers 402 with its price; the client signs a USDC payment for that amount and sends the same request again. There is nothing to sign up for, which suits AI agents that hold a wallet. With a key, nothing changes: calls spend your account's credits as before.
| Pays in | USDC on Solana (solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp), and on Base (eip155:8453) when the 402 lists it |
| Protocol | x402 version 2, scheme exact |
| Facilitator | PayAI, which checks each payment and puts it on chain. It pays the network fee: the wallet needs USDC only. |
Prices
| Route | MCP tool | Price |
|---|---|---|
| POST /analyze | analyze_address | $0.05, $0.15 above 1,000 transactions |
| POST /trace | trace_transfer | $0.10 |
| POST /insiders | find_token_insiders | $0.10 |
| POST /follow | — | $0.10 |
| POST /victims | — | $0.15 |
The same as credits from a pack: a credit is $0.05. The price depends on the request (a deeper analysis costs more), so the 402 comes once the request has been checked: a bad address is a 400, never a payment.
How a call is paid
- Ask. Send the request with no key. The answer is
402, with aPAYMENT-REQUIREDheader: base64 JSON with the amount (USDC has 6 decimals, so50000is $0.05), the asset, the wallet to pay and the network. - Pay. Sign a transfer of that amount and send the same request again with it in a
PAYMENT-SIGNATUREheader. Nothing has moved yet. - Get the result. Iris checks the payment, runs the work, and only then settles the payment. The
200carries aPAYMENT-RESPONSEheader with the transaction that paid.
1curl -si -X POST https://iriscan.app/api/analyze \2 -H "Content-Type: application/json" \3 -d '{ "address": "sy88tvipKfaCTuVVeU2PczPa88hqgPfKnYQyCHboHP8" }' | grep -i '^payment-required' | cut -d' ' -f2 | tr -d '\r' | base64 -d1{2 "x402Version": 2,3 "error": "Payment required: $0.05 in USDC, paid per call with x402 (or use an Iris API key: https://iriscan.app/account?tab=api)",4 "resource": { "url": "https://iriscan.app/api/analyze", "mimeType": "application/json", "description": "…" },5 "accepts": [6 {7 "scheme": "exact",8 "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",9 "amount": "50000",10 "asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",11 "payTo": "…",12 "maxTimeoutSeconds": 300,13 "extra": { "feePayer": "…" }14 }15 ]16}A failed call costs nothing
An error, a timeout or an analysis that could only return labels (meta.scope database) is never settled: the signed payment is simply not used. One exception on Solana: a signed payment expires about a minute after it was made, so a call still running 30 seconds after it was checked is settled then, and stays paid if it fails after that.A client in TypeScript
The official packages handle the 402 for you: they read the price, sign the payment and retry.
1npm install @x402/fetch @x402/svm @solana/kit1import { decodePaymentResponseHeader, wrapFetchWithPaymentFromConfig } from "@x402/fetch";2import { ExactSvmScheme, toClientSvmSigner } from "@x402/svm";3import { createKeyPairSignerFromBytes, getBase58Encoder } from "@solana/kit";4 5// A Solana wallet with USDC in it. It needs no SOL: the facilitator pays the fee.6const wallet = await createKeyPairSignerFromBytes(7 getBase58Encoder().encode(process.env.SOLANA_PRIVATE_KEY!),8);9 10const paidFetch = wrapFetchWithPaymentFromConfig(fetch, {11 schemes: [{ network: "solana:*", client: new ExactSvmScheme(toClientSvmSigner(wallet)) }],12});13 14const res = await paidFetch("https://iriscan.app/api/analyze", {15 method: "POST",16 headers: { "Content-Type": "application/json" },17 body: JSON.stringify({ address: "sy88tvipKfaCTuVVeU2PczPa88hqgPfKnYQyCHboHP8" }),18});19const profile = await res.json();20 21// The USDC transaction that paid for this call.22const { transaction } = decodePaymentResponseHeader(res.headers.get("PAYMENT-RESPONSE")!);To pay from Base instead, register ExactEvmScheme from @x402/evm for eip155:*, when the 402 lists Base.
Errors and limits
| Status | Code | Means |
|---|---|---|
| 402 | payment_required | No payment yet, or the one sent was refused: the error in PAYMENT-REQUIRED says why (insufficient_funds, a simulation that failed, a payment for another price). Sign a new one. |
| 402 | settlement_failed | The work was done but the payment couldn't be settled: the result is held back. PAYMENT-RESPONSE says why; sign a new payment to try again. |
| 400 | invalid_payment | PAYMENT-SIGNATURE is not base64 JSON. |
A payment is used once: the same one sent again while it is being settled is refused. A wallet runs at most three calls at once, like an account; the other errors and limits are the same.