Skip to main content
GET
Audit a contract
Audit any contract on a supported chain. Send a GET request with your API key in the X-Auth-Key header; the full parameter and response reference is below. By default the call is synchronous: it returns the cached verdict in milliseconds, or holds until a first-time audit completes. Add async=true to make first-time audits non-blocking: a cached verdict is still returned directly, but when an audit has to run you get { "status": "FETCHING" | "DECOMPILING" | "ANALYZING" } immediately instead. Poll the same URL every 1-2 seconds until it flips to { "audit": ... }, useful to drive a progress indicator. Billing is identical in both modes; progress responses are free. Or skip polling entirely: add subscribe=true to fire the audit, subscribe the token, and have the finished verdict (plus every later change) pushed to you over a live SSE stream or a webhook. See SSE & Webhooks. address also accepts a liquidity pool (a Uniswap V2/V3 pair address or a Uniswap V4 pool id) or a V4 hook address. audit.entry tells you how it was addressed (token / pool / hook).
  • Pool. Audits the token plus that specific pool’s hook, instead of the token’s primary pool. The response is the token’s audit: audit.address is the resolved token, and audit.pool describes the pool the hook verdict was computed against (including whether it is the token’s primary pool). Store results keyed by audit.chain + audit.address, not by the value you sent.
  • Hook. Returns the hook’s own audit, not a token’s (one hook often serves many tokens): audit.address is the hook, isSafe, description and vulnerabilities are the hook’s verdict, and audit.hookContext lists the pools and tokens that use it (pools, poolCount). For a token’s combined verdict, call with the token address and read isHookSafe / hookAudit.
A pool id or hook that resolves nowhere returns 404 { "reason": "pool_not_found" | "hook_not_found" }. For how to read the result, see Understanding Results. For the full list of type values, see Risk Categories.

Solana tokens

Pass chain=solana (or sol) and the token’s mint address as address. A Solana address is base58 and case-sensitive: send it exactly as written, never lowercased (a lowercased mint is a different account). The response echoes it unchanged in audit.address, so key your cache on it as-is.
A Solana token has no contract code of its own: it is a mint account of the SPL Token or Token-2022 program. Its risks are the mint’s configuration: who can mint, who can freeze accounts, and which Token-2022 extensions are on. So a Solana audit makes no LLM call and reads no code. The verdict is computed deterministically from the mint’s live onchain state, re-read on every refresh, and returned with sourceType: "solana" and the snapshot it was read from in solanaState.

What is checked

We judge the capability, not what the holder did with it so far: a live power to block a sale, seize balances, or raise a fee without a ceiling is critical whoever holds it (a wallet, a multisig or a program). A live mint authority is a warning: it can dilute holders but cannot block or seize their tokens, so on its own it leaves the token SAFE. The description names the holder in plain words. A power is cleared only when it is revoked or held by a burn address, and then it raises no finding at all. A scheduled fee change already written onchain counts at its future rate. Each Solana finding carries solanaFlag (the control, e.g. freeze_authority) and solanaHolder (the address holding it); code is empty. When a live power is held by the known issuer of that token (a regulated stablecoin issuer on its own stablecoin, for example) or by a program whose code we reviewed and which bounds the power, the finding stays listed with mitigated: true and a gateReason naming the reviewed party, and it no longer counts against isSafe. That never applies to a state actively used against holders: a pause that is on, accounts born frozen, a prohibitive fee.

What is not checked

  • No code review and no LLM. There is no code to read.
  • No market analysis. Holders, liquidity, volume, insiders and deployer history are not part of the verdict.
  • The onchain sell simulation and the LP-lock warning are not available on Solana yet. When they are, they will appear exactly as on EVM chains.
  • Pool and hook entries are EVM only. On Solana, address must be the mint.

The solanaState object

solanaState is the snapshot the verdict was computed from (snake_case, like b20Flags), null on every other chain. Every power is an authority object { address, kind } where kind is revoked, burned, multisig (with m of n signers, and keys when fewer distinct keys than m are enough), wallet, pda (a program-controlled address, with its owner program) or program (the address of a deployed program, whose deploy key may still sign). The main fields: program (spl-token or spl-token-2022), mint_authority, freeze_authority, permanent_delegate, transfer_fee, transfer_hook, pausable, default_account_state, non_transferable, scaled_ui_amount, metadata, and unix_ts / read_slot (when it was read). The full schema is below.

Not a token mint

An address that is not a fungible token mint (a wallet, a pool, a token account, a program, an NFT) returns 404 { "audit": null, "reason": "not_a_token", "message": "..." }, the same shape as the no-pool 404: branch on reason. Do not retry it: send the token’s mint address instead.

Push updates

Subscriptions work the same on Solana: subscribe with chain: "SOLANA" and the mint. Because the configuration is re-read on every refresh, a change such as an authority revoked, a fee raised or a pause switched on is pushed as an audit.changed event like any other verdict change.

Authorizations

X-Auth-Key
string
header
required

Your secret API key. Create one from your dashboard.

Query Parameters

chain
enum<string>
required

Chain the contract is deployed on, as a case-insensitive symbol. See Supported Chains. SOLANA (alias SOL) audits a Solana token by its mint address.

Available options:
ROBINHOOD,
BSC,
BASE,
ETH,
AVAX,
STABLE,
ARC,
ARB,
HYPE,
MONAD,
PLASMA,
POLYGON,
MEGAETH,
OP,
LINEA,
TEMPO,
MANTLE,
ABSTRACT,
SONIC,
BLAST,
APE,
XLAYER,
INK,
UNICHAIN,
STORY,
ZKEVM,
SCROLL,
ZKSYNC,
SOLANA
address
string
required

The subject to audit: a token address, a liquidity pool (a Uniswap V2/V3 pair address or a Uniswap V4 pool id, 32 bytes), or a Uniswap V4 hook address. A pool resolves to its token: the response's audit.address is that resolved token and audit.pool describes the pool the hook verdict was computed against. A hook returns the hook's own audit: audit.address is the hook and audit.hookContext lists the pools and tokens that use it. audit.entry tells you how it was addressed. Cache your results by audit.chain + audit.address, not by the value you sent. On chain=solana: the token's base58 mint address, CASE-SENSITIVE (send it exactly as written; a lowercased mint is another account). Pool and hook entries are EVM only.

Pattern: ^(?:0x(?:[a-fA-F0-9]{40}|[a-fA-F0-9]{64})|[1-9A-HJ-NP-Za-km-z]{32,44})$
pool
string

Optional, EVM only. Pin a specific pool for a token entry: address is the token, pool is the pool whose hook drives the verdict (a V2/V3 pair address or a V4 pool id). A mismatch (the pool does not trade the token) is a 400. Not used on chain=solana (it answers 404 pool_not_found): send the mint alone.

Pattern: ^0x(?:[a-fA-F0-9]{40}|[a-fA-F0-9]{64})$
allow_decompile
enum<string>
default:true

Closed-source contracts are audited on decompiled bytecode (the most expensive tier). Set false to opt out: closed-source contracts then answer { "audit": null, "reason": "decompile_disabled" } instead of being decompiled, and cached decompiled verdicts are not served. Sent as the string "true"/"false" in the query.

Available options:
true,
false
async
enum<string>
default:false

Async mode. false (default): the request holds until the audit is done, classic synchronous behavior. true: if no cached audit exists, the audit is started in the background and the response is { status }; poll the SAME URL every 1-2s until it flips to { audit } (typically 10-30s for a first-time audit). There is no job id: an audit is idempotent per (chain, address), so the token address is the job handle. Billing is identical in both modes; progress responses are never billed. With subscribe=true you do not even poll: the finished verdict is pushed to you (over your SSE stream or webhook) the moment it lands (see SSE & Webhooks).

Available options:
true,
false
subscribe
enum<string>
default:false

Subscribe this (chain, address) to push updates as part of this call. Once subscribed, any later verdict change (safe↔unsafe flip, owner change, a gate or honeypot flipping, or a closed-source token re-audited on its now-verified source) is pushed to you over a live SSE stream or a webhook, instead of you polling. Combined with async=true, the finished audit result is itself pushed when ready, so you fire the call and just consume the events. Subscribing works with or without a delivery transport configured; with neither, pull the changes from GET /api/events (see SSE & Webhooks). Charges the one-time subscribe fee for a genuinely-new token; idempotent for an already-subscribed one.

Available options:
true,
false

Response

The audit verdict, wrapped in an audit object.

The response shape.

audit
object
required

The audit result.

billing
object

Echo of what this call was billed, present only for authenticated API-key callers (never for anonymous/browser traffic). type is the bill category (e.g. cached, fresh_no_decompile, fresh_with_decompile, refresh) or null when the call is free; credits is the amount metered.