Docs
API reference
Two live endpoints on Solana mainnet, free during the beta, no key. /api/explain says what happened in a transaction, what it cost and what to do next. /api/errors is the public error catalog.
Overview
JSON over HTTPS at https://www.unstucksol.com/api. Both endpoints read Solana mainnet live, need no key and no sign-in, and are free during the beta.
- GET
/explain?sig=Explain a transactionFree in beta - POST
/explainThe same, with a JSON bodyFree in beta - GET
/errorsPublic error catalogFree
Price
- Today
- Free. Beta: calls are free; the x402 flow is the planned paid tier and is not enforced.
- Planned
- 0.10 USDC per explain call over x402, in USDC on Solana, with no account. The catalog stays free. How it will work
Machine-readable: openapi.yaml · SKILL.md · llms.txt · MCP server card (planned)
Try it now
Paste this into a terminal. It asks about a real swap on mainnet that failed on its slippage limit.
$ curl 'https://www.unstucksol.com/api/explain?sig=3cbDDFD5GrPVzJZCfP8ppSGiTFHd6RHQBbgkrgRpXLbu2fbSPdKdgjimJduvMhRP3N8yh6QznSmtSVbh3feUwV2R'
Response 200 (program logs trimmed)
{
"signature": "3cbDDFD5GrPVzJZCfP8ppSGiTFHd6RHQBbgkrgRpXLbu2fbSPdKdgjimJduvMhRP3N8yh6QznSmtSVbh3feUwV2R",
"status": "failed",
"title": "Slippage limit exceeded",
"program": "Jupiter v6",
"via": null,
"raw_error": "{\"InstructionError\":[5,{\"Custom\":6001}]}",
"raw_message": "custom program error: 0x1771",
"decoded": "SlippageToleranceExceeded (6001)",
"plain": "The price moved more than your slippage limit allowed, so the swap was cancelled.",
"cost": { "sol": 0.00002191, "note": "Network fee only (incl. 0.00001691 SOL priority fee). Nothing was swapped or sent." },
"balance_changes": [
{ "asset": "SOL", "mint": null, "delta": -0.00002191, "delta_ui": "-0.00002191", "note": "network fee" },
{ "asset": "PUMP", "mint": "pumpCmXqMfrsAkQ5r49WcJnRayYRqmXz6ae8H7H9Dfn", "delta": 0, "delta_ui": "0", "note": "unchanged" },
{ "asset": "USDC", "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "delta": 0, "delta_ui": "0", "note": "unchanged" }
],
"fix": { "action": "retry", "params": { "fresh_quote": true }, "safe": true },
"handoff": false,
"slot": 456949936,
"block_time": 1792005100,
"explorer": "https://solscan.io/tx/3cbDDFD5GrPVzJZCfP8ppSGiTFHd6RHQBbgkrgRpXLbu2fbSPdKdgjimJduvMhRP3N8yh6QznSmtSVbh3feUwV2R",
"fee_payer": "4B8srvotmWddTZuMt4RrBQrnx1sQj2Z7FaXgLbWvpVdd",
"programs": ["System Program", "Token Program", "Jupiter v6"],
"error_logs": ["Program JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 failed: custom program error: 0x1771"],
"logs": ["…the last 12 program log lines…"],
"audience": "user"
}
GET /explain
What happened in a transaction, what it cost and what to do next. Live, free during the beta.
Send a signature, or an explorer link that contains one, as ?sig=. POST /explain takes the same fields as JSON. Answers are cached for 5 minutes.
Request
-
sigstringRequiredA mainnet transaction signature in base58 (87 or 88 characters), or an explorer link with
/tx/<signature>in it.signatureworks as the parameter name too. Devnet and testnet links are refused with a400. -
audience"user" | "agent"Default"user"Who reads the answer. It is echoed back in the response; in the beta both values return the same fields.
POST /explain with content-type: application/json and a body like {"signature": "<signature>", "audience": "agent"}.
Responses
-
200ExplanationThe answer. Fields below.
-
404ExplanationThe signature isn’t on mainnet: an Explanation with
status: "not_found", a plain answer and afixto send it again. -
400{ error, usage }Missing or malformed signature, a wallet address instead of a signature, a devnet link, or an
audienceother thanuseroragent.errorsays which, in plain words. -
429{ error }Too many lookups from one address, or the Solana RPC is busy. Wait for
Retry-Afterseconds. -
502 · 504{ error }The Solana RPC failed or took too long. Try again in a moment.
-
405 · 500 · 503{ error }Wrong method (use GET or POST), an unexpected error, or the service isn’t available.
Response fields: Explanation
-
signaturestringThe signature you asked about (taken out of the link if you sent one).
-
status"failed" | "succeeded" | "not_found"What happened to the transaction.
succeededanswers say what it did, like a swap or a transfer. -
titlestringA short name for what happened, e.g.
Slippage limit exceededorSwap on Jupiter v6. -
program·viastring · string | nullThe program the answer is about, and the program that called it when the failure happened one level down.
-
raw_error·raw_messagestring | nullThe chain’s error object as JSON text, and the error as wallets print it, e.g.
custom program error: 0x1771.nullwhen nothing failed. -
decodedstring | nullError name and code, decoded against the program that failed, e.g.
SlippageToleranceExceeded (6001). -
plainstringWhat happened, in plain words. Quote it to people as it is.
-
cost{ sol, note }SOL the transaction cost the fee payer (network fee, priority fee and tips), and what that covers.
-
balance_changesarrayUp to 8 of the fee payer’s balance changes:
asset,mint,delta(number),delta_ui(exact decimal text) andnote. Negative means it left the wallet. -
fix{ action, params, safe }A fix your code can apply: parameters, never a transaction.
-
action"retry" | "add_sol" | "use_token_account" | "none"What kind of fix it is.
-
paramsobjectThe exact settings. Possible keys:
slippage_bps,fresh_quote,refresh_route,compute_unit_limit,fresh_blockhash,sol,mint. -
safebooleantruefor retries with a stated setting and for adding a little SOL to the same wallet. Apply a fix automatically only when this istrue. What safe means
-
-
handoffbooleanReserved for topics a person should decide, like risk, refunds or account access. Always
falsein the beta. -
slot·block_timenumber | nullWhere the transaction landed, and when (Unix seconds).
-
explorer·fee_payer·programsstring · string | null · string[]A Solscan link, the wallet that paid the fee, and the programs the transaction ran.
-
error_logs·logsstring[]Up to 3 log lines that mention the error, and the last 12 program log lines.
-
audience"user" | "agent"The audience you asked for.
Examples
curl
$ curl 'https://www.unstucksol.com/api/explain?sig=3cbDDFD5GrPVzJZCfP8ppSGiTFHd6RHQBbgkrgRpXLbu2fbSPdKdgjimJduvMhRP3N8yh6QznSmtSVbh3feUwV2R'
curl, POST
$ curl -X POST https://www.unstucksol.com/api/explain \
-H 'content-type: application/json' \
-d '{"signature":"3cbDDFD5GrPVzJZCfP8ppSGiTFHd6RHQBbgkrgRpXLbu2fbSPdKdgjimJduvMhRP3N8yh6QznSmtSVbh3feUwV2R","audience":"agent"}'
JavaScript
const res = await fetch("https://www.unstucksol.com/api/explain?sig=" + encodeURIComponent(signature));
const answer = await res.json();
if (!res.ok && answer.status !== "not_found") throw new Error(answer.error); // 400, 429, 5xx: plain-words error
console.log(answer.plain); // quote it to people as it is
if (answer.fix.safe) await applyFix(answer.fix); // parameters, never a transaction
else await askAPerson(answer);
GET /errors
The public error catalog: the raw Solana error, decoded, in plain words, with the typical fix. Live and free, no key.
Query parameters
-
qstringOptionalOnly entries that contain this text, like
0x1771,rentorPump.fun. Leave it out to get the whole catalog.
Response 200
An array of entries. The catalog is cached for an hour.
-
rawstringThe error as the chain reports it, e.g.
custom program error: 0x1771. -
decodedstringName and code, e.g.
SlippageToleranceExceeded (6001). -
plainstringThe same situation in plain words.
-
typical_fixstringWhat usually fixes it.
-
program·categorystringWhich program the code belongs to (codes mean different things in different programs), and the kind of problem, e.g.
slippage.
$ curl 'https://www.unstucksol.com/api/errors?q=0x1771'
Response (first of three entries)
[
{
"raw": "custom program error: 0x1771",
"decoded": "SlippageToleranceExceeded (6001)",
"plain": "The price moved more than your slippage limit allowed, so the swap was cancelled.",
"typical_fix": "Retry with a fresh quote and a slightly higher slippage limit (at most double, up to 3%).",
"program": "Jupiter v6",
"category": "slippage"
},
…
]
Public error catalog
A sample of the entries GET /api/errors returns, word for word. The full catalog is one request away: /api/errors. Public error strings only, no signatures, wallets or messages from users.
9 sample entries
| Raw | Decoded | Plain | Typical fix |
|---|---|---|---|
Slippage limit exceededcustom program error: 0x1771 |
SlippageToleranceExceeded (6001) · Jupiter v6 |
The price moved more than your slippage limit allowed, so the swap was cancelled. |
Retry with a fresh quote and a slightly higher slippage limit (at most double, up to 3%). retry · slippage_bps, fresh_quote |
Blockhash expiredBlockhash not found |
TransactionError: BlockhashNotFound |
Your transaction waited too long and expired. Nothing was sent — just try again. |
Send it again with a fresh blockhash. retry · fresh_blockhash |
Not enough SOL for rentTransaction results in an account with insufficient funds for rent |
TransactionError: InsufficientFundsForRent |
This needed a small refundable SOL deposit to open an account, and the wallet was short. Add a little SOL and retry. |
Add about 0.003 SOL, then retry. add_sol · sol |
No SOL for feesAttempt to debit an account but found no record of a prior credit. |
TransactionError: AccountNotFound |
This wallet has no SOL for the network fee yet. Every transaction needs a tiny amount of SOL. |
Send a little SOL to the wallet first (0.01 SOL covers many fees). add_sol · sol |
Not enough tokenscustom program error: 0x1 |
InsufficientFunds (1) · Token Program |
There weren’t enough tokens in the account for this amount, so nothing moved. Check the balance and try a smaller amount. |
Check the token balance and send or swap a smaller amount. none |
Wrong token accountcustom program error: 0x2 |
InvalidMint (2) · Token Program |
The tokens were pointed at a token account that doesn’t match this coin or this wallet, so nothing moved. |
Use the wallet’s own associated token account for this mint. use_token_account · mint |
Compute limit exceededComputational budget exceeded |
InstructionError: ComputationalBudgetExceeded |
The transaction ran out of compute before it finished, so nothing happened. Retrying with a higher compute limit usually works. |
Retry with a higher compute unit limit. retry · compute_unit_limit |
Token account frozencustom program error: 0x11 |
AccountFrozen (17) · Token Program |
This token account is frozen by the token’s issuer, so these tokens can’t move. Only the issuer can unfreeze it. |
Nothing to retry. Only the token’s freeze authority can unfreeze the account. none |
Bonding curve completecustom program error: 0x1775 |
BondingCurveComplete (6005) · Pump.fun |
This coin has finished its Pump.fun bonding curve and now trades in a regular pool, so the curve can’t fill this trade. Trade it on PumpSwap or through Jupiter. |
Trade the coin on its pool (PumpSwap) or through Jupiter. none |
No sample entry matches that. Try 0x1771 or rent, or search the full catalog with /api/errors?q=.
Codes are per program: 0x1771 is 6001, and 6001 means different things in different programs. Unstuck decodes the code against the program that failed.
Limits
- Price
- Free during the beta. No keys, no free-tier counters.
- Rate limit
- Roughly 30 explain lookups a minute per IP address. Over that,
429 Too Many RequestswithRetry-After. - Caching
- Answers are cached for 5 minutes, the catalog for an hour.
- Network
- Solana mainnet only.
What is logged: security model.
Planned
Not live yet. This is the intended shape, so you can plan for it.
Paid tier: x402
Beta: calls are free; the x402 flow is the planned paid tier and is not enforced.
x402 puts the price in the HTTP response, so there is no account and no key. The plan: 0.10 USDC per explain call, paid in USDC on Solana; the catalog stays free.
- 01
Ask
Send the request as usual. Without payment, the API answers
402 Payment Requiredwith the price in thePAYMENT-REQUIREDheader. - 02
Pay and ask again
Your client signs a USDC transfer of exactly
amounttopayToand sends the same request again with it inX-PAYMENT. - 03
Get the answer and the receipt
The transfer settles on Solana. The API answers
200with the explanation and the settlement signature inX-PAYMENT-RESPONSE.
-
PAYMENT-REQUIREDresponse · 402JSON:
scheme"exact",network"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"(mainnet),asset"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"(USDC),amount"100000"(0.10 USDC, 6 decimals),payToandresource. -
X-PAYMENTrequestThe signed USDC transfer, base64, sent with the same request.
-
X-PAYMENT-RESPONSEresponse · 200The settlement transaction signature: the receipt.
MCP server and skill package
Use the HTTP API today (curl / fetch); the MCP server and skill package ship when the API leaves beta. The planned tools mirror the two endpoints: unstuck.explain(signature) and unstuck.errors(query). SKILL.md already describes the live HTTP calls.