getPlexa
live · 4 payment rails

Economic safety layer
for trading agents

x402-native, pay-per-call. Two HTTP tools an agent pays for inline — no accounts, no keys held. Quotes on Base, Polygon & Arbitrum — pay in USDC from any of them or Solana.

x402-native multichain decision-grade
the gap
A wallet guard like Blockaid tells an agent whether a transaction is malicious — can I sign this? It can't tell the agent should I trade this? — what price actually fills under size, and whether the token is an economic trap. Plexa answers the economic question.
two endpoints, one engine
POST /v1/quote$0.02
Executable liquidity under a given size: real fill price (not mid/spot), price impact (bps), realizable depth, per-leg route, worst-case slippage, confidence. Priced from canonical on-chain quoters, on Base, Polygon & Arbitrum.
POST /v1/pretrade/check$0.05
avoid — only when a trap is proven at that block: nowhere to sell, less than 5% of your size sitting in the pool as the counter asset, or trading switched off by the contract itself. Otherwise clear — no proven traps, which is not “safe”. Plus risk_profile: age, concentration, depth, the amount a sale would be paid from — as numbers; the call is yours. Analysis runs on Base today; you can pay from any of the four rails. Same endpoint, same price, also at /v1/token-safety and /v1/rug-check.
The first call from a new wallet is free — the full response, every field, same engine as a paid one. One free call per wallet, on either endpoint; nothing is settled on-chain for it.
example
// POST /v1/pretrade/check
{ "token": "0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf", "sizeUSD": "1000", "chain": "base" }

// live capture, Base block 49798026–49798027 — fields trimmed to the ones explained below
// →
{
  "verdict": "clear",
  "triggers": [],
  "confidence": "high",
  "risk_profile": {
    "age": { "ageDays": 710.56 },
    "concentration": { "top1SharePct": 82.2985, "top10SharePct": 90.2837, "holdersExact": false, "ageSec": 140, "stale": false },
    "liquidity": { "poolCount": 45, "venueCount": 13, "exitLiquidityUsd": 40275191.6 },
    "oracle": { "source": "chainlink:BTC / USD", "deviationBps": 2 },
    "transferability": { "hasBlacklist": true, "fingerprints": "isBlacklisted" }
  },
  "quote": { "direction": "buy", "executablePrice": 0.00001564358, "depthAtSize": 0.01564358, "priceImpactBps": 5.16 },
  "validUntil": 1786385405501,
  "coverage": { "axesExpected": 5, "axesReporting": 5 },
  "liquidityCoverage": {
    "conclusive": true, "poolsFound": 45, "factoriesChecked": 13,
    "unmeasuredVenues": 0, "unmeasuredVenueNames": [], "venuesUnread": 0,
    "singletonPoolsSeen": 10, "singletonPoolsCounted": 1, "singletonPoolsRejected": 8,
    "singletonPoolsHooked": 7, "singletonScanTruncated": true,
    "asOfBlockFirst": 49798026, "asOfBlock": 49798027
  }
}
A real capture, trimmed to the fields explained here — nothing invented. executablePrice is how much token 1 USDC buys at the requested size: the real fill, not a mid. validUntil says until when asking again returns this same answer; coverage says how many of the five axes reported. liquidityCoverage is the exit check showing its work: conclusive: true means it reached a verdict-grade conclusion; 45 pools were found across 13 factories with none left unread. In Uniswap v4, 10 pools of this pair were seen and the sale was simulated in each — 1 accepted it, 8 refused (a refusal is a measurement about that pool, not a gap in our data), 7 run a hook inside the swap, and singletonScanTruncated admits the scan hit its work cap. The two block numbers are the start and end of those reads: one apart here, so no single block describes the answer exactly, and that gap is the uncertainty. In concentration, ageSec: 140 with stale: false means the holder numbers were measured 140 seconds earlier and served from cache. Absent numbers come back null with a reason, never as 0. Trimmed away here but present in every response: reasons (why this verdict, in plain language), flags (the five raw signals behind the axes), ts (when we computed it, epoch ms) and disclaimer.
what else comes back
// live capture, WETH, 2026-08-20 14:46:29Z, Base block 50223921 — the six context blocks, verbatim; the notes each block carries are long and are cut here
{
  "identity": { "name": "Wrapped Ether", "symbol": "WETH", "decimals": 18, "totalSupply": 239296.58651918 },
  "valuation": { "fdvExecutableUsd": 543916397.6557496, "basis": "totalSupply(this chain) x executablePrice(at sizeUSD)" },
  "ownership": { "ownerAddress": null, "ownerRenounced": null, "isMintable": false, "creatorSharePct": 1.2e-07 },
  "dormancy": { "topHolderIdleDays": null, "lastTopHolderMoveBlock": null },
  "market": { "priceUsdSpot": 2277.76, "volume24hUsd": 551324354.8699999, "marketCapUsd": 542587444, "holderCount": 5223863 },
  "sources": {
    "*": "measured",
    "risk_profile.concentration": "unavailable:holder-axis-produced-nothing",
    "flags.F_CONC": "unavailable:holder-axis-produced-nothing",
    "valuation": "derived:identity.totalSupply*quote.executablePrice",
    "dormancy": "unavailable:holder-axis-produced-no-block",
    "ownership.creatorAddress": "derived:sender-of-first-transfer",
    "ownership.isMintable": "derived:mint-selector-in-bytecode",
    "market": "vendor:dexscreener+goplus",
    "market.priceUsdSpot": "vendor:dexscreener",
    "market.volume24hUsd": "vendor:dexscreener",
    "market.marketCapUsd": "vendor:dexscreener",
    "market.holderCount": "vendor:goplus"
  }
}
A second live capture, not the first one edited — the token above was measured on 10.08, before these blocks existed, and re-taking it today would have cost the fifth axis. Both are real; neither is patched. valuation.fdvExecutableUsd is fully-diluted value at the price your size executes at — deliberately a different number from market.marketCapUsd, which is the vendor's global figure: for a bridged token ours is legitimately smaller, and the gap is bridged supply plus the spot-vs-executable spread. ownerRenounced: null means no owner() answered — unknown is not renounced. dormancy is null here and says why: it rests on the holder axis, and that axis produced no block for this token. And sources is the part no market-data endpoint ships: every field signed with who produced it — derived, measured, unavailable, vendor. unavailable:<reason> names the hole instead of hiding it, which is how «we could not check» stays distinguishable from «we checked and it is fine».
connect & configure
As an MCP tool
Add the Plexa MCP server to any agent (Claude Code / Claude Desktop / any MCP host). It exposes plexa_quote and plexa_pretrade_check. Payment is handled automatically via x402 from a wallet you configure — Plexa never sees the key.
As an agent skill
An Agent Skill, pretrade-exit-check, teaches an agent when the pre-trade check is worth its $0.05 and how to read the answer — the verdict vocabulary, what liquidityCoverage proves, and which fields are allowed to be null. Install: npx skills add plexa-xyz/skills.
Direct HTTP (x402)
Any x402 client works (e.g. @x402/fetch). Call the endpoint; on 402 the client signs a USDC payment (EIP-3009) and retries — the response is served only after on-chain settle. Pass chain: base / polygon / arbitrum.
// MCP client config (claude_desktop_config.json or any host)
{
  "mcpServers": {
    "plexa": {
      "command": "npx",
      "args": ["-y", "@getplexa/mcp"],
      "env": {
        "PLEXA_BASE_URL": "https://api.getplexa.com",
        "AGENT_WALLET_KEY": "${AGENT_WALLET_KEY}",
        "CHAIN": "base"
      }
    }
  }
}
Use a dedicated, low-balance wallet. This key signs payments; it is not an API key. Keep it in your environment and let the host expand ${AGENT_WALLET_KEY} — if your MCP host does not support expansion, put the literal value in a file with restrictive permissions and never commit it. Plexa never sees the key: it stays in your client.
Live on npm: @getplexa/mcp — run with npx -y @getplexa/mcp. The public API is live at api.getplexa.com.
for agents
Everything below is machine-readable and free — no payment, no key. Start at llms.txt: it carries the endpoints, the payment handshake and the full response envelope, so an agent knows what it gets before it pays.
// machine entry points — all on the API host, all GET, all free
GET https://api.getplexa.com/llms.txt              // text/plain — the manifest
GET https://api.getplexa.com/openapi.json          // OpenAPI 3.1, both endpoints + the 402 flow
GET https://api.getplexa.com/.well-known/x402      // x402 service discovery pointer
GET https://api.getplexa.com/contact               // who runs this, how to reach them

// the paid endpoints themselves — POST only; an unpaid POST answers 402 with the terms
POST https://api.getplexa.com/v1/quote             // $0.02
POST https://api.getplexa.com/v1/pretrade/check    // $0.05 (also /v1/token-safety, /v1/rug-check)
The docs live on api.getplexa.com, not on this domain — this one serves the page you are reading. A GET on a paid path answers 405 with instructions rather than a bare error, and an unpaid POST answers 402 with the payment terms in the PAYMENT-REQUIRED header — both are legitimate first moves for an agent that has never seen this API.
open-source reference agent

See Plexa in action

A complete, MIT-licensed reference agent — a dust consolidator that discovers small token balances, checks each through Plexa, and swaps the safe ones. Client-only: it uses nothing but the public API, so it doubles as a working integration pattern you can fork.

plexa-xyz/agent
Live on GitHub: plexa-xyz/agent — MIT-licensed, client-only.