{"openapi":"3.1.0","info":{"title":"Plexa API","version":"1.0.0","description":"Economic-safety API for trading agents: executable (realizable) DEX quotes and pre-trade token verdicts, paid per-call via x402 v2 (USDC, no API key). Informational data, not financial advice. First call from a new wallet is FREE — the full response, every field, same engine as a paid call. No trial tier, no reduced answer: only the COUNT is limited (one per wallet). Sign the x402 payment as usual; nothing is settled on-chain for that first call.","contact":{"name":"Plexa","url":"https://getplexa.com","email":"support@getplexa.com"}},"servers":[{"url":"https://api.getplexa.com"}],"paths":{"/v1/quote":{"post":{"operationId":"quote","summary":"Executable swap quote at size ($0.02 per call, x402)","description":"REALIZABLE fill price for the full requested size (not a mid/spot feed): executablePrice, priceImpactBps, depthAtSize, worstCaseSlippageBps, per-leg route, confidence — from canonical on-chain quoters on Base/Polygon/Arbitrum.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tokenIn","tokenOut"],"additionalProperties":false,"anyOf":[{"required":["sizeIn"]},{"required":["sizeUSD"]}],"properties":{"tokenIn":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$"},"tokenOut":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$"},"sizeIn":{"type":"string","pattern":"^(?:0\\.\\d*[1-9]\\d*|[1-9]\\d*(?:\\.\\d+)?)$","maxLength":78},"sizeUSD":{"type":"string","pattern":"^(?:0\\.\\d*[1-9]\\d*|[1-9]\\d*(?:\\.\\d+)?)$","maxLength":78},"chain":{"type":"string","enum":["base","polygon","matic","arbitrum","arbitrum-one","arb","eip155:8453","eip155:137","eip155:42161"]}}},"example":{"tokenIn":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","tokenOut":"0x4200000000000000000000000000000000000006","sizeUSD":"5000","chain":"base"}}}},"responses":{"200":{"description":"Executable quote for the full requested size","content":{"application/json":{"schema":{"type":"object","properties":{"chain":{"type":"string"},"tokenIn":{"type":"string"},"tokenOut":{"type":"string"},"amountIn":{"type":"string","description":"atomic units priced"},"amountOut":{"type":"string","description":"atomic units realizable at size"},"executablePrice":{"type":"number","description":"REALIZABLE fill price (tokenOut per tokenIn) at the requested size — not a mid/spot feed"},"spotPrice":{"type":["number","null"],"description":"Size-independent mid of the deepest qualifying venue. P16: null when no defensible mid exists (a pool with no in-range liquidity still quotes a slot0 mid; we publish absence rather than that number)."},"priceImpactBps":{"type":["number","null"],"description":"Derived from spotPrice — null exactly when spotPrice is null."},"depthAtSize":{"type":"number"},"worstCaseSlippageBps":{"type":["number","null"],"description":"Measured against spotPrice — null exactly when spotPrice is null."},"route":{"type":"object","description":"per-leg execution route (dex/pool/fee tier)"},"confidence":{"type":"string","enum":["high","medium","low"]},"flags":{"type":"array","items":{"type":"object"}},"blockNumber":{"type":"string"},"decimals":{"type":"object"},"disclaimer":{"type":"string"},"ts":{"type":"number"},"validUntil":{"type":"number","description":"Epoch ms until which asking again returns THIS answer: ts + the freshness window of the shortest-lived input (pool state). It is a statement about our recomputation, NOT a promise that the price has not moved."}}},"example":{"chain":"base","tokenIn":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","tokenOut":"0x4200000000000000000000000000000000000006","amountIn":"5000000000","amountOut":"2638683932811238569","executablePrice":0.0005277367865622476,"spotPrice":0.0005272182760762905,"priceImpactBps":0,"depthAtSize":2.6386839328112384,"worstCaseSlippageBps":0,"route":{"dex":"univ3","path":["0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","0x4200000000000000000000000000000000000006"],"pools":["0xd0b53D9277642d899DF5C87A3966A349A798F224"],"legs":[{"dex":"univ3","venue":"uniswap-v3","pool":"0xd0b53D9277642d899DF5C87A3966A349A798F224","tokenIn":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","tokenOut":"0x4200000000000000000000000000000000000006","feePips":500}]},"confidence":"high","venuesConsidered":6,"blockNumber":"49616390","decimals":{"in":6,"out":18},"disclaimer":"Informational on-chain liquidity data, not financial advice. Prices reflect pool state at the stated block and may move before execution.","ts":1786022127490,"validUntil":1786022130490}}}},"400":{"description":"Invalid input (strict schema: 0x addresses; at least one of sizeIn/sizeUSD — if both are provided, sizeUSD takes precedence). Body includes detail + exampleRequest + docs; rejected requests are never charged."},"402":{"description":"Payment required (x402 v2). The payment requirements (amount, USDC asset, payTo, accepted networks) are base64-JSON-encoded in the PAYMENT-REQUIRED response header. Sign an EIP-3009 USDC authorization (EVM networks) or submit a partially-signed SPL USDC transfer transaction (Solana) and retry the same request with the PAYMENT-SIGNATURE header (npm @x402/fetch automates this). The JSON body is an informative summary only — the header is authoritative.","headers":{"PAYMENT-REQUIRED":{"description":"base64(JSON) x402 v2 payment requirements: {x402Version, resource, accepts[]}","schema":{"type":"string"}}}},"429":{"description":"Rate limited"}}}},"/v1/pretrade/check":{"post":{"operationId":"pretradeCheck","summary":"Pre-trade economic-safety verdict ($0.05 per call, x402)","description":"Two-level economic-safety answer for a token on Base. Level 1 — verdict avoid|clear: \"avoid\" = a listed trap was PROVEN at this block (see `triggers`); \"clear\" = none of them fired. \"clear\" is NOT a safety rating and NOT a prediction — structural risk lives in `risk_profile`. The trap list is closed: NO_EXIT_VENUE, EXIT_LIQUIDITY_DRAINED, TRADING_DISABLED. Level 2 — risk_profile: age, holder concentration (ANY token age: top-1/top-10 share computed by us from balances + on-chain totalSupply; data age in the response; vendor outage → \"unknown\" with a reason, never a clean default), liquidity structure incl. exitLiquidityUsd (the counter asset a sell would be paid from), oracle availability, transfer restrictions — as measured DATA with plain-language notes, not severities. Plus confidence, reasons, the 5 raw signals and an executable quote sub-result. confidence: On \"avoid\" this is confidence IN THE TRIGGER, which is a measurement taken this call, so it is always \"high\" — a check that failed elsewhere does not make the measured trap less true. On \"clear\" it is how COMPLETE the checks were: fewer axes computed, lower confidence. Either way the gaps are reported explicitly — see `reasons` and the null fields in `risk_profile`. verdict semantics changed 2026-08-01 (v2): avoid = provable trap only; ok/caution are gone. Alias paths for /v1/pretrade/check: /v1/token-safety, /v1/rug-check — the SAME endpoint (same engine, same price, same request and response, one shared free trial), not a different product. They exist because agent catalogs index by URL path.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["token"],"additionalProperties":false,"properties":{"token":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$"},"sizeUSD":{"type":"string","pattern":"^(?:0\\.\\d*[1-9]\\d*|[1-9]\\d*(?:\\.\\d+)?)$","maxLength":78},"chain":{"type":"string","enum":["base","polygon","matic","arbitrum","arbitrum-one","arb","eip155:8453","eip155:137","eip155:42161"]}}},"example":{"token":"0x4200000000000000000000000000000000000006","sizeUSD":"1000"}}}},"responses":{"200":{"description":"Economic-safety verdict + executable quote sub-result","content":{"application/json":{"schema":{"type":"object","properties":{"chain":{"type":"string"},"token":{"type":"string"},"verdict":{"type":"string","enum":["avoid","clear"],"description":"\"avoid\" = a listed trap was PROVEN at this block (see `triggers`); \"clear\" = none of them fired. \"clear\" is NOT a safety rating and NOT a prediction — structural risk lives in `risk_profile`."},"triggers":{"type":"array","items":{"type":"string","enum":["NO_EXIT_VENUE","EXIT_LIQUIDITY_DRAINED","TRADING_DISABLED"]},"description":"Which proven traps fired. Empty whenever verdict is \"clear\". This is the CLOSED list — nothing outside it can produce \"avoid\"."},"confidence":{"type":"string","enum":["high","medium","low"],"description":"On \"avoid\" this is confidence IN THE TRIGGER, which is a measurement taken this call, so it is always \"high\" — a check that failed elsewhere does not make the measured trap less true. On \"clear\" it is how COMPLETE the checks were: fewer axes computed, lower confidence. Either way the gaps are reported explicitly — see `reasons` and the null fields in `risk_profile`."},"reasons":{"type":"array","items":{"type":"string"}},"risk_profile":{"type":"object","description":"Structural risk as DATA, not as a judgement: age, concentration, liquidity (incl. exitLiquidityUsd — the counter asset a sell would be paid from), oracle availability, transfer restrictions. Each section carries measured fields plus a plain-language `note`. Absent numbers are null (could not measure), never 0. `risk_profile.concentration` carries `ageSec` and `stale`. `ageSec: null` with `stale: false` means the holder data was read on this call. A number in `ageSec` means it was measured that many seconds ago; `stale: true` additionally means the holder source was unreachable right now, so the LAST KNOWN measurement is reported instead of nothing, and `confidence` drops. `stale: null` means there is no holder value at all in this response — neither fresh nor stored — so there is nothing for the freshness flag to describe. The stored value survives a restart and keeps its real age. Concentration is level-2 structural data — no verdict trigger reads it — so an aged value cannot change `avoid`/`clear`."},"flags":{"type":"object","description":"5 raw signals: F-LIQ, F-CONC, F-AGE, F-FOT, O-SANITY"},"quote":{"type":["object","null"],"description":"executable USDC→token quote at sizeUSD, or null if unpriceable"},"sizeUSD":{"type":"string"},"disclaimer":{"type":"string"},"ts":{"type":"number"},"validUntil":{"type":"number","description":"Epoch ms until which asking again returns THIS answer: ts + the freshness window of the shortest-lived input the answer rests on (pool state, which feeds the exit pot). A statement about our recomputation, not about the market."},"coverage":{"type":"object","description":"How complete this answer is, in one comparison: axesReporting of axesExpected axes produced a result. The detail stays where it was — `reasons`, `flags[].severity === \"unknown\"` and the null fields of `risk_profile`.","properties":{"axesExpected":{"type":"number"},"axesReporting":{"type":"number"}}},"liquidityCoverage":{"type":"object","description":"It also carries `liquidityCoverage` {conclusive, poolsFound, factoriesChecked, unmeasuredVenues, unmeasuredVenueNames, venuesUnread, singletonPoolsSeen, singletonPoolsCounted, singletonPoolsRejected, singletonPoolsHooked, singletonScanTruncated, asOfBlockFirst, asOfBlock}: `conclusive: true` with `poolsFound: 0` means every covered factory was queried and no pool exists — that is a measurement, and it is what raises NO_EXIT_VENUE. `conclusive: false` means an AMM we cannot measure pool-by-pool (Uniswap v4 and other singleton AMMs) holds this token, so the exit triggers are not raised on this call. `poolsFound: null` with `factoriesChecked: 0` means discovery did not run. For singleton AMMs the sell is SIMULATED per pool, and the three outcomes are separate counts: `singletonPoolsCounted` (simulation succeeded, proceeds are inside exitLiquidityUsd), `singletonPoolsRejected` (the chain rejected the sale — a measurement about that pool, not a gap in our data), and pools left unquestioned because the scan hit its work cap, flagged by `singletonScanTruncated`. `singletonPoolsSeen` is the total found and `singletonPoolsHooked` how many run a hook inside the swap. All five are null only when the liquidity axis produced no metrics at all. `asOfBlockFirst` and `asOfBlock` are the Base block heights at the start and the end of the liquidity reads: equal means the axis was read inside one block and is reproducible at that block, different means no single block describes it and the gap is the size of that uncertainty.","properties":{"conclusive":{"type":"boolean","description":"Whether the exit check reached a CONCLUSION — i.e. whether we are entitled to state that no exit exists. false does not mean nothing was measured: the pools found and the pot are still in risk_profile.liquidity and are correct."},"poolsFound":{"type":["number","null"],"description":"Pools found on the covered factories. null = discovery did not run (NOT zero pools)."},"factoriesChecked":{"type":"number","description":"How many factories were queried. 0 = we did not look at all."},"unmeasuredVenues":{"type":"number","description":"Venues that hold this token but do not yield to our measurement."},"unmeasuredVenueNames":{"type":"array","items":{"type":"string"},"description":"Their identifiers."},"venuesUnread":{"type":["number","null"],"description":"Of those, venues that simply did not answer on THIS call (upstream node), as opposed to AMMs we cannot measure by design. Non-zero means retrying may return a complete answer. null = the liquidity axis produced no metrics at all."},"singletonPoolsSeen":{"type":["number","null"],"description":"Singleton-AMM pools of this pair found via the Initialize event (hooked ones included). null = the liquidity axis produced no metrics at all."},"singletonPoolsCounted":{"type":["number","null"],"description":"Of those, pools whose sell simulation SUCCEEDED — their proceeds are inside exitLiquidityUsd."},"singletonPoolsRejected":{"type":["number","null"],"description":"Of those, pools whose sell simulation was REJECTED by the chain. That is a measurement — this pool cannot be sold into — not a gap in our data."},"singletonPoolsHooked":{"type":["number","null"],"description":"Of those, pools whose hook address carries swap permissions, so the hook runs inside the sale. Derived from the address bits, no RPC call."},"singletonScanTruncated":{"type":["boolean","null"],"description":"true = the singleton scan hit its work cap and some pools were left unqueried. Reported whether or not it changed the answer."},"asOfBlockFirst":{"type":["number","null"],"description":"Base block height when the liquidity axis STARTED reading. null = the block could not be read."},"asOfBlock":{"type":["number","null"],"description":"Base block height when it FINISHED. Equal to asOfBlockFirst means the whole axis was read inside one block and is reproducible there; different means no single block describes it, and the gap is the size of that uncertainty."}}}}},"example":{"chain":"base","token":"0x4200000000000000000000000000000000000006","verdict":"clear","triggers":[],"confidence":"medium","reasons":["no provable trap detected. This is NOT a safety rating: it means none of the checks that can PROVE a trap (NO_EXIT_VENUE, EXIT_LIQUIDITY_DRAINED, TRADING_DISABLED) fired at this block. Structural risk — age, holder concentration, liquidity depth, transfer restrictions — is reported in risk_profile and can still be severe","could not check F_CONC — reported as unknown, never as clean; confidence reduced accordingly"],"risk_profile":{"age":{"ageDays":1143.6,"genesisBlock":381217,"note":"1143.6 days since the first transfer — an established history, which says nothing about today's liquidity"},"concentration":{"top1SharePct":null,"top10SharePct":null,"holders":null,"holdersExact":null,"source":null,"asOfBlock":null,"ageSec":null,"stale":null,"note":"holder concentration could not be computed for this token — not a clean result, an absent one"},"liquidity":{"poolCount":32,"venueCount":13,"largestPoolSharePct":68.32,"exitLiquidityUsd":81553087.89,"note":"32 pool(s) found across 13 venue(s), holding $81553087.89 of counter asset (WETH+USDC) — that is the pot your sell would be paid from"},"oracle":{"source":"chainlink:ETH / USD","deviationBps":16,"stalenessSec":1186,"note":"cross-checked against chainlink:ETH / USD; the pool mid differs from the feed by 16 bps"},"transferability":{"sellTaxRaw":null,"buyTaxRaw":null,"hasBlacklist":false,"hasMaxLimit":false,"tradingEnabled":null,"fingerprints":"none","note":"no tax, blacklist, limit or trading-gate function was found. Absence of these fingerprints is a negative result on a known list of patterns — not proof that the token is sellable"}},"quote":{"direction":"buy","tokenIn":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","tokenOut":"0x4200000000000000000000000000000000000006","executablePrice":0.0005214424247520731,"priceImpactBps":0,"depthAtSize":0.5214424247520731,"worstCaseSlippageBps":0,"confidence":"high"},"sizeUSD":"1000","disclaimer":"Informational economic-safety signal, not financial advice. verdict \"clear\" means NO PROVABLE TRAP was detected at this block — it is not a safety rating and not a prediction; read risk_profile for structural risk. verdict \"avoid\" means a listed trap was conclusive. Checks are heuristics over on-chain state and can miss novel traps.","ts":1786358976434,"validUntil":1786358979434,"coverage":{"axesExpected":5,"axesReporting":4},"liquidityCoverage":{"conclusive":true,"poolsFound":32,"factoriesChecked":13,"unmeasuredVenues":0,"unmeasuredVenueNames":[],"venuesUnread":0,"singletonPoolsSeen":1,"singletonPoolsCounted":1,"singletonPoolsRejected":0,"singletonPoolsHooked":0,"singletonScanTruncated":false,"asOfBlockFirst":49784813,"asOfBlock":49784814}}}}},"400":{"description":"Invalid input"},"402":{"description":"Payment required (x402 v2). The payment requirements (amount, USDC asset, payTo, accepted networks) are base64-JSON-encoded in the PAYMENT-REQUIRED response header. Sign an EIP-3009 USDC authorization (EVM networks) or submit a partially-signed SPL USDC transfer transaction (Solana) and retry the same request with the PAYMENT-SIGNATURE header (npm @x402/fetch automates this). The JSON body is an informative summary only — the header is authoritative.","headers":{"PAYMENT-REQUIRED":{"description":"base64(JSON) x402 v2 payment requirements: {x402Version, resource, accepts[]}","schema":{"type":"string"}}}},"422":{"description":"Chain not supported for pretrade yet (Base only)"},"429":{"description":"Rate limited"}}}},"/health":{"get":{"operationId":"health","summary":"Liveness (free)","responses":{"200":{"description":"ok"}}}}}}