Agents and developers
API reference
Every endpoint of wisp-x402, its price, what it returns and its errors, with a curl call and a minimal paying client.
This page is in English.
For developers building agents or tools. x402 is a standard for paying per request over HTTP: the server answers 402 Payment Required with a price, and the client pays and asks again.
Base URL: https://wisp-x402.fly.dev. All endpoints are GET. Network: Solana mainnet (solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp).
Endpoints
| Path | Paid | Price |
|---|---|---|
/x402/rug-check/{mint} | Yes | $0.005: 5000 USDC units, or $WiSP at the discount below |
/burns | No | |
/health | No | |
/.well-known/x402.json | No |
USDC mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v. $WiSP mint: USEu5qroduzuvrSZfvs3xM1MwyU83joapQF5LMyWiSP. The $WiSP amount is quoted fresh in each 402 from the market price: 25% off the dollar price, plus a 2% margin for price movement, so about 23.5% below the USDC price; at 13:45 UTC on 24 October 2026 a rug check cost 41.434464 $WiSP.
Paying
A request without payment returns 402 with an empty JSON body and a payment-required header: base64 JSON listing what the service accepts.
curl -i https://wisp-x402.fly.dev/x402/rug-check/USEu5qroduzuvrSZfvs3xM1MwyU83joapQF5LMyWiSP
Decoded, the header looks like this (addresses shortened):
{
"x402Version": 2,
"error": "Payment required",
"resource": { "url": "https://wisp-x402.fly.dev/x402/rug-check/USEu5q…WiSP", "mimeType": "application/json" },
"accepts": [
{ "scheme": "exact", "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
"amount": "5000", "asset": "EPjFWdd5…Dt1v", "payTo": "4r2e4D2x…bra6",
"maxTimeoutSeconds": 300, "extra": { "feePayer": "9vxA1Q5f…dG5k" } },
{ "scheme": "exact", "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
"amount": "41434464", "asset": "USEu5q…WiSP", "payTo": "4r2e4D2x…bra6",
"maxTimeoutSeconds": 300, "extra": { "feePayer": "9vxA1Q5f…dG5k" } }
]
}
Your client signs a transfer for one of the offers and repeats the request with the payment header. The service checks it, works out the answer, and settles the payment on Solana only when the answer is a success: it then returns 200. A malformed address is refused with 400 before any 402, so there is nothing to sign. An address that is not a token mint gets 404 after your payment is checked, and that payment is not settled, so nothing is charged.
A payment pays for one answer. Sending the same signed payment again is refused (payment_already_used) and not charged: a payment's transaction is public once it lands, so it cannot work as a key. If the network is slow to confirm, the server checks the chain before answering, so a payment that landed still gets its answer in the same request.
A minimal client (JavaScript)
Uses the x402 reference packages, @x402/core and @x402/svm 2.28.0, and @solana/kit:
import { x402Client, x402HTTPClient } from '@x402/core/client';
import { registerExactSvmScheme } from '@x402/svm/exact/client';
import { createKeyPairSignerFromBytes } from '@solana/kit';
const NETWORK = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp';
const WISP = 'USEu5qroduzuvrSZfvs3xM1MwyU83joapQF5LMyWiSP';
const signer = await createKeyPairSignerFromBytes(secretKeyBytes); // your agent's 64-byte Solana secret key
const YOUR_RPC_URL = 'https://…'; // any Solana mainnet RPC
const mint = 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263'; // the token to check
const core = new x402Client();
registerExactSvmScheme(core, { signer, networks: [NETWORK], rpcUrl: YOUR_RPC_URL,
policies: [(version, offers) => offers.filter(o => o.asset === WISP)] }); // pay in $WiSP
// A standard client pays only its default stablecoins until told otherwise:
core.setSpendControls({ allowedAssets: [{ network: 'solana:*', asset: WISP, maxAmountPerPayment: '1000000000' }] });
const http = new x402HTTPClient(core);
const url = 'https://wisp-x402.fly.dev/x402/rug-check/' + mint;
const first = await fetch(url); // 402
const required = http.getPaymentRequiredResponse(name => first.headers.get(name), await first.json());
const payload = await http.createPaymentPayload(required);
const paid = await fetch(url, { headers: http.encodePaymentSignatureHeader(payload) });
console.log(paid.status, await paid.json());
To pay in USDC, filter for the USDC mint instead and drop setSpendControls.
Rug check
GET /x402/rug-check/{mint} reads the mint account and its largest holders.
| Field | Type | Meaning |
|---|---|---|
mint | string | The address you asked about |
readAt | string | ISO time of the read |
ok | boolean | true |
program | string | token or token-2022 |
decimals | number | |
supply | string | Raw supply |
flags | array | { key, active } for mint, freeze, permanentDelegate, transferFee, transferFeeChangeable, transferHook, pausable, nonTransferable, defaultFrozen, metadataMutable |
allows | array | One sentence per active flag, such as "Someone can freeze holders' tokens." |
top10Percent | number or null | Share held by the ten largest token accounts, not counting a pump.fun curve. null when it could not be read |
pumpCurve | object or null | While the token is on a pump.fun bonding curve: { address, tokenAccount, percent }, the curve, the account holding its unsold supply and that share of the supply. null when the curve holds none (graduated, or never on pump.fun) or the holder list could not be read |
summary | string | The sentences joined, or a line saying nobody has special powers |
Example, for $WiSP on 24 October 2026. Here top10Percent is null because the RPC used for the example refused the largest-holders read:
{
"mint": "USEu5qroduzuvrSZfvs3xM1MwyU83joapQF5LMyWiSP",
"readAt": "2026-10-24T13:47:27.880Z",
"ok": true,
"program": "token-2022",
"decimals": 6,
"supply": "931910509125656",
"flags": [ { "key": "mint", "active": false }, { "key": "freeze", "active": false }, … ],
"allows": [],
"top10Percent": null,
"pumpCurve": null,
"summary": "Its rules give nobody special powers over holders' tokens: no minting, freezing, fees, hooks or forced transfers."
}
The check reports what the token's rules allow. It never calls a token safe.
Free endpoints
/burns:{ mint, burnedRaw, burned, burns, last }, wherelastlists up to 20 recent burns withat,amountandsignature./health:{ ok, payTo, feePayer, wisp }.wispistruewhile $WiSP is accepted./.well-known/x402.json: the service's name, description and resources.
Errors
| Status | Body or reason | Cause |
|---|---|---|
| 400 | {"error":"Invalid mint"} | The path is not a base58 address of 32 bytes. Answered before any 402, nothing to pay |
| 404 | {"error":"Not a token mint"} | The address is not a token mint. Your payment is not settled |
| 404 | Unknown path | |
| 402 | payer_daily_limit | More than 300 paid calls from one payer in 24 hours |
| 402 | service_daily_limit | More than 3,000 paid calls in total in 24 hours |
| 402 | service_paused | Five failed settlements in ten minutes pause payments for 15 minutes |
| 402 | payment_already_used | The same payment was sent for a different path |
| 402 | payment_in_progress | The same payment is still settling |
| 402 | transaction_too_large, lookup_tables_not_allowed, fee_payer_mismatch, invalid_transaction | The payment transaction is malformed |
Related
Last checked against the app: 24 October 2026, build bf09a3f