Getting started
Quickstart
Three steps: see a price, pay it, parse the JSON. Any x402-capable client does the middle step for you — the examples below use the official SDK and the pay CLI.
0 · A wallet with USDC#
Payments are USDC on Solana mainnet. The keypair needs USDC only — the x402 facilitator pays the network fee, so a balance of 0 SOL is fine. A few dollars covers hundreds of calls. No wallet yet? Fund your agent walks through both ways — sign in with Google or email, or fund a keypair yourself; new to crypto entirely, start at Paying, plainly.
Nothing to sign up for with us
1 · Read the price (free)#
Every paid route answers an unauthenticated request with 402 and a base64 PAYMENT-REQUIRED header. Decode it to see the amount in USDC base units, the mint and the pay-to address.
$ curl -i https://citable.run/v1/keyword-suggest?seed=seo+api
HTTP/2 402
payment-required: eyJ4NDAyVmVyc2lvbiI6MiwiZXJyb3IiOiJQYXltZW50IHJlcXVpcmVkIiwi…
$ curl -s https://citable.run/v1/keyword-suggest?seed=seo+api -D - -o /dev/null | grep -i payment-required | cut -d' ' -f2 | base64 -d | jq .accepts[0]
{
"scheme": "exact",
"network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
"amount": "1000", // USDC base units → $0.001
"asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC mint
"payTo": "6nWrWRTqrqpqbu5MsMW8mPDpZjKLwPb9Wf8BdMwNMWzK",
"maxTimeoutSeconds": 300
}2 · Pay with the x402 SDK#
wrapFetchWithPayment turns fetch into a client that reads the 402, signs a USDC transfer for the quoted amount, and retries. The settlement transaction comes back in PAYMENT-RESPONSE.
// bun add @x402/fetch @x402/svm @solana/kit
import { wrapFetchWithPayment, decodePaymentResponseHeader, x402Client } from "@x402/fetch";
import { registerExactSvmScheme } from "@x402/svm/exact/client";
import { toClientSvmSigner } from "@x402/svm";
import { createKeyPairSignerFromBytes } from "@solana/kit";
const keypair = await createKeyPairSignerFromBytes(secretKey); // a wallet holding USDC
const client = registerExactSvmScheme(new x402Client(), { signer: toClientSvmSigner(keypair) });
const paidFetch = wrapFetchWithPayment(fetch, client);
const res = await paidFetch("https://citable.run/v1/onpage-audit?url=https://example.com");
const data = await res.json();
const settlement = decodePaymentResponseHeader(res.headers.get("PAYMENT-RESPONSE")!);
console.log(data.score, settlement.transaction); // 92, 5AS7BF…Two things the server does on purpose
recentBlockhash — the client fetches its own, so the challenge can be retried. And payTo is a USDC token account that already exists; no account-creation rent is ever passed to you.Or: the pay CLI#
Any x402 client works the same way. With pay.sh installed and funded:
$ pay curl https://citable.run/v1/keyword-metrics?keywords=seo+api
{ "keywords": [ { "keyword": "seo api", "volume": 390, "cpc": 20.98, … } ], … }3 · Errors are free#
Only a delivered result is settled. A 400 for bad params, a 502 because a data source did not answer, a 503 because an endpoint is not configured — none of them charge anything. Each endpoint page lists its codes.
| Status | Meaning | Charged |
|---|---|---|
| 200 | Result delivered | yes — the quoted price |
| 402 | No valid payment on the request; the header carries the price | no |
| 4xx | Bad parameters — the message says which | no |
| 502 / 503 | Source unavailable, or not configured on this deployment | no |