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 inUSDC on Solana (solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp), and on Base (eip155:8453) when the 402 lists it
Protocolx402 version 2, scheme exact
FacilitatorPayAI, which checks each payment and puts it on chain. It pays the network fee: the wallet needs USDC only.

Prices

RouteMCP toolPrice
POST /analyzeanalyze_address$0.05, $0.15 above 1,000 transactions
POST /tracetrace_transfer$0.10
POST /insidersfind_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 a PAYMENT-REQUIRED header: base64 JSON with the amount (USDC has 6 decimals, so 50000 is $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-SIGNATURE header. Nothing has moved yet.
  • Get the result. Iris checks the payment, runs the work, and only then settles the payment. The 200 carries a PAYMENT-RESPONSE header with the transaction that paid.
402.sh
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 -d
PAYMENT-REQUIRED
1{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.

terminal
1npm install @x402/fetch @x402/svm @solana/kit
paid.ts
1import { 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

StatusCodeMeans
402payment_requiredNo 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.
402settlement_failedThe 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.
400invalid_paymentPAYMENT-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.