Unbyte
Docs

API docs (beta)

Decode Solana transactions, check one before signing, and read program descriptions and IDLs, over HTTPS or MCP.

Unbyte's API and services are in beta. Plans, limits and prices may change before general availability.

Base URL

Base URL
https://api.unbyte.dev
  • Every answer is JSON. It carries Unbyte-Registry-Version, the IDL registry release it was decoded with, and Unbyte-Schema: 1.0.
  • POST bodies are JSON with a Content-Length header, up to 256 KB.
  • CORS is open to every origin, so a browser can call the API directly.
  • The same routes are described for tools as OpenAPI 3.1 at https://api.unbyte.dev/openapi.json.

Authentication

Every endpoint works without a key. Anonymous calls are limited per IP: 60 requests a minute, and 30 a minute to MCP. With a key, calls count against your plan and get its limits.

Header
Authorization: Bearer ub_live_…
  • A key is ub_live_ followed by 32 base58 characters. You create keys after signing in with a Solana or EVM wallet.
  • A key is shown once, when it is created: store it as a secret. Unbyte keeps only a hash of it.
  • A key can be limited to the scopes decode, verify and idl, and given an expiry. A revoked key stops working within a minute.
  • An Authorization header that is not a valid key is 401 invalid_key; leave the header out to call anonymously.

The examples below read your key from UNBYTE_API_KEY; drop the -H "Authorization: …" line to try them anonymously.

Endpoints

GET/v1/health

Whether the API is up and which registry it serves. Never counted against a plan.

Request
curl https://api.unbyte.dev/v1/health
Response
FieldDescription
oktrue when the API answers.
registry_versionThe IDL registry release answers are decoded with.
idls_loadedHow many IDLs are ready to decode with.
rpc.modenormal, or cache_only while reads from Solana are paused.

GET/v1/tx/{signature}

Decode a confirmed transaction by its base58 signature. Scope decode; one call. Add ?raw=1 for each instruction's data (hex) and account keys.

Request
curl "https://api.unbyte.dev/v1/tx/$SIGNATURE" \
  -H "Authorization: Bearer $UNBYTE_API_KEY"
Response
FieldDescription
signature, slot, failed, fee_payerThe transaction as it landed.
version"legacy", 0 or 1.
instructions[]Every instruction, top-level and inner. position is the top-level index, then the inner index; status is decoded, event, borrowed (read with the IDL of a matching program, named in source_program), no_idl, idl_unusable or error.
name, args, accounts[]On a decoded instruction: its name, its arguments, and its accounts with name, address, writable and signer.
idl_source, trailing_bytesWhich IDL decoded it (registry, chain or history), and how many bytes it left undescribed; 0 means every byte is accounted for.
log_events[]Events the programs emitted through their logs.
balance_changes[]Net change per account: account, owner, mint (null for SOL), delta (a decimal string in base units) and decimals.
registry_versionThe registry release used.

A finalized report never changes, so it carries an ETag: send it back in If-None-Match to get 304 Not Modified, which is not counted.

Errors: invalid_signature for a malformed signature, not_found when Solana does not know the transaction.

POST/v1/decode/tx

Decode transaction bytes you already hold, signed or not. Scope decode; one call.

Request
curl https://api.unbyte.dev/v1/decode/tx \
  -H "Authorization: Bearer $UNBYTE_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"transaction\": \"$TX_BASE64\"}"
Body
FieldDescription
transactionRequired. The serialized transaction, base64 or base58: legacy and v0 up to 1,232 bytes, v1 up to 4,096.
slotDecode with the IDL versions live at this slot; reported back as slot.
loaded_addresses{ writable: [], readonly: [] }: the addresses a v0 transaction loads from lookup tables. Without them those accounts show as ?<index>.
rawtrue adds each instruction's data and account keys.
Response
FieldDescription
reportThe same report as GET /v1/tx, with top-level instructions only and no balance changes: nothing has run yet.

POST/v1/verify

Check an unsigned transaction before signing it, against what the signer expects. Scope verify; one verify check. Unbyte does not store the transactions you verify.

Request
curl https://api.unbyte.dev/v1/verify \
  -H "Authorization: Bearer $UNBYTE_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"transaction\": \"$TX_BASE64\", \"intent\": {\"max_slippage_bps\": 100}}"
Body
FieldDescription
transactionRequired. The unsigned transaction, base64 or base58.
intentOptional: max_in, in_mint, min_out, out_mint (amounts as decimal strings in base units, mints as addresses or "SOL"), max_slippage_bps (0 to 10,000), allow_programs (up to 64 program IDs) and signer.
resolve_lookupstrue reads v0 lookup tables from Solana so every account is named.
Response
FieldDescription
verdictok, warn (a medium finding) or block (a high or critical finding).
findings[]code, severity (info, low, medium, high, critical), message, instruction (its position), program_id, data and confidence (high or low).
flowsWhat leaves the signer: sol_out (lamports), fees, swaps[], approvals[] and closes[].
transactionversion, fee_payer, programs and lookups_resolved.
decodedThe decoded transaction, as in the report above, without balance changes.
intent, registry_version, disclaimerThe intent checked against, the registry release, and the limits of the check.

Finding codes: sol_out, sol_drain, token_transfer, token_approve, close_account, set_authority, program_upgrade, swap_amount_in, swap_min_out, swap_semantics_unknown, no_min_out, slippage_high, amount_in_exceeds_intent, min_out_below_intent, in_mint_mismatch, out_mint_mismatch, intent_mismatch, unknown_program, unknown_instruction, closed_program, program_not_allowed, extra_signers, durable_nonce, lookup_unresolved, decode_incomplete and simulation_skipped.

Simulation is not live yet: every report carries the simulation_skipped finding, so findings come from the decoded instructions only.

GET/v1/programs/{program_id}

Describe a Solana program. Scope idl; one call.

Request
curl https://api.unbyte.dev/v1/programs/JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 \
  -H "Authorization: Bearer $UNBYTE_API_KEY"
Response
FieldDescription
program_id, nameThe program and its name; name_inferred is true when Unbyte named it.
category, kindWhat the program is, such as a router.
idlsource (registry, chain or none), version, spec, fingerprint, and the state of the program's own on-chain IDL in chain.
healthHow well the program's traffic decodes, from the public dashboard.
activityix_per_day, share_of_app and the snapshot_day they were measured on.
swap, meanings, noteFor routers: how to read their swap arguments, what each field means, and any caveat.

GET/v1/programs/{program_id}/idl

The IDL Unbyte decodes a program with. Scope idl; one call.

Request
curl https://api.unbyte.dev/v1/programs/pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA/idl \
  -H "Authorization: Bearer $UNBYTE_API_KEY"
Response
FieldDescription
bodyThe IDL itself: the registry IDL as published, else the program's on-chain IDL.

It carries an ETag; a matching If-None-Match gets 304 Not Modified, which is not counted. not_found means the program has no IDL Unbyte can use.

Errors

A failed call answers with an HTTP error status and one envelope. The message is fixed per code and never repeats your input. 429 and most 503 answers carry Retry-After, in seconds.

Error
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests; retry later."
  }
}
Error codes
CodeStatusMeaning
invalid_request400The request is not valid: a malformed body, field or program ID.
invalid_signature400The signature must be a base58 64-byte transaction signature.
invalid_transaction400The transaction could not be parsed.
invalid_key401The API key is malformed, unknown, revoked or expired.
quota_exceeded402The monthly quota is used up; upgrade the plan.
insufficient_credits402The credit balance cannot pay for this call.
forbidden403The key lacks the scope for this call, or the account is suspended.
not_found404Not found.
length_required411POST requests need a Content-Length header.
payload_too_large413The input is larger than the limit.
rate_limited429Too many requests; retry after Retry-After seconds.
internal500Internal error.
rpc_unavailable502No Solana RPC provider could answer.
rpc_budget_exhausted503Reads from Solana are paused; retry later.
unavailable503The API is paused; retry later.
db_unavailable503The account database is unavailable; retry later.
rpc_timeout504Solana RPC providers did not answer in time.

Rate limits

Each key gets its plan's rate and monthly allowance. Prices and overage are on the pricing page.

Limits by plan
PlanRateCalls a monthVerify checksKeys
Anonymous60/min per IP–––
Free2/s50,0001001
Builder10/s500,0005,0003
Pro25/s1,000,00050,00010
Scale100/s5,000,000250,00025
  • Past the monthly allowance, Free stops with 402 quota_exceeded. Paid plans pay overage from credits, per call, and get 402 insufficient_credits when the balance runs out.
  • Over the rate, a call gets 429 rate_limited with Retry-After. Calls that read from Solana, such as a transaction by signature, can be limited further per IP or per key.
  • Calls rejected as malformed and 304 answers are not counted.

MCP for agents

The same calls as tools for an AI agent, over MCP's Streamable HTTP transport. The server is stateless: no session, one JSON-RPC message per POST (no batches).

Endpoint
https://api.unbyte.dev/mcp
Tools
ToolInputReturns
decode_txsignature, or transaction (base64 or base58); optional slot, loaded_addresses, rawThe report of GET /v1/tx or POST /v1/decode/tx
verify_before_signtransaction; optional intent, resolve_lookupsThe report of POST /v1/verify
describe_programprogram_idThe answer of GET /v1/programs/{program_id}
get_idlprogram_idThe IDL; over 256 KB it is idl_too_large, fetch it from the REST route
  • A result is at most 256 KB: a longer decode keeps the first instructions and says so with truncated and instructions_omitted.
  • A failed tool call returns isError: true with the same error envelope.
  • Results are on-chain data: token names, memos and IDL names are third-party text. Have your agent treat them as data, never as instructions.
  • Without a key, MCP allows 30 requests a minute per IP.

Most MCP clients take a remote server in this shape; leave out headers to connect anonymously.

MCP client config
{
  "mcpServers": {
    "unbyte": {
      "type": "http",
      "url": "https://api.unbyte.dev/mcp",
      "headers": {
        "Authorization": "Bearer ub_live_…"
      }
    }
  }
}