x402 Index & Smart Order Router
Agent402 is not only a seller of 500+ tools - it's also a routing layer for the whole x402 ecosystem. It crawls public x402 sellers, tracks their health, and lets a buyer ask "find me the cheapest healthy tool that does X" across every seller it knows about.
Three surfaces, all free (mounted outside the paywall - discovery primitives
shouldn't cost money, by the same logic as /api/find):
| Surface | What it returns |
|---|---|
GET /marketplace |
HTML dashboard: every seller indexed, tool count, network, last-fetched time, rolling health, discovery sources. (GET /index is the old path and now answers 301 → /marketplace; follow redirects or just use /marketplace.) |
GET /api/index |
JSON snapshot of the same data: per-seller health, routable, rolling history, totals |
POST /api/route |
Smart Order Router / neutral x402 discovery API: { query, top, include } → top-N matching tools across sellers, ranked by match score, then health, then price. include = all (default) / external (exclude Agent402 itself) / local |
GET /api/leaderboard |
On-chain ranking of every seller by Base USDC settled volume - see x402-Leaderboard |
And one paid executing surface built on the same resolver:
| Surface | What it does |
|---|---|
POST /api/route/execute ($0.01) |
Resolve a task description (or explicit slug) to the best-matching catalog tool and run it in the same call. Returns {result, receipt} - the receipt names the dispatched slug, its price (capped at $0.005 underlying), and how it was resolved. Underlying tool errors pass through with their own status codes. Wallet-only. |
POST /api/route/execute-plus ($0.05) |
Same contract, mid budget: underlying tools priced up to $0.04 - the proportional rung, so a $0.02 tool costs $0.05 through the router rather than $0.55. |
POST /api/route/execute-max ($0.55) |
Same contract, top budget: underlying tools priced up to $0.50. |
POST /api/route/execute-pro ($3.30) |
Same contract for the report-sized tools: underlying tools priced up to $3.00. |
The execution tier ladder
Four rungs, because one flat routing fee cannot cover both a $0.001 utility and a $3 report without overcharging almost every buyer:
| Route | You pay | Covers an underlying tool priced |
|---|---|---|
POST /api/route/execute |
$0.01 | ≤ $0.005 |
POST /api/route/execute-plus |
$0.05 | ≤ $0.04 |
POST /api/route/execute-max |
$0.55 | ≤ $0.50 |
POST /api/route/execute-pro |
$3.30 | ≤ $3.00 |
Pick the cheapest rung that covers the tool. You do not have to guess:
POST /api/route already quotes the right one per result in executeVia
({tool, price, underlyingPriceUsd, routingFeeUsd}), and asking a rung to run
something above its cap returns a self-correcting 409 naming the exact tier
that covers it, or the tool's direct route. A 409 is a 4xx, so it cancels
settlement: an under-budget attempt costs nothing.
External execution: run any proven x402 or MPP seller in one call
With include: "external", route-execute goes beyond this host's catalog: it resolves the best external seller for the task - an x402 seller from the open index, or an MPP seller on Tempo from the live-verified MPP marketplace - pays them from Agent402's own spending wallet over the seller's wire (x402 or MPP), and relays the result. One payment from you, one request, cross-vendor and cross-protocol settlement underneath.
The reliability filter is the point. The open ecosystem is full of endpoints that answer a 402 but never deliver a paid result, so route-execute only considers sellers with proven settled volume (on Base: on-chain completed deliveries on the x402-Leaderboard; on Algorand: verification counts witnessed by the GoPlausible facilitator; on Tempo/MPP: recent inbound USDC.e transfers to the seller's own recipient, read on-chain) and then probes the candidate for a live 402 before committing. External settlement is chain-matched: pay on Base and the router pays a Base seller from its Base spending wallet; pay on Algorand and it pays an Algorand seller from its AVM wallet; pay over MPP on Tempo and it pays a Tempo/MPP seller from its Tempo wallet - the chain you fund is the chain it spends. (An operator can additionally let Base buyers fall through to Tempo/MPP sellers.) A payment on a chain without a spending wallet gets an honest 409 naming the supported chains, and a 4xx/5xx always cancels your settlement, so you are never charged for a failure. Relayed bodies are marked untrustedContent (the seller's output, not ours). Default remains local-only: external routing is an explicit opt-in.
The full walkthrough - tiers, selection mechanics, chain matching, and a real production receipt with both on-chain settlement transactions - is the Smart Order Router guide.
How a seller gets into the Index
- Local catalog - the Agent402 server's own tools are always present (no network).
- Operator seeds - origins listed in the
X402_INDEX_SEEDSenv (comma-separated) get crawled every 30 minutes. - Auto-discovery - every hour, the indexer pulls public x402 registries (currently the Coinbase CDP Bazaar) and adds new origins to the crawl set, capped at 50,000 sellers as a sanity guard. Crawls run through a worker pool with a concurrency limit (
CRAWL_CONCURRENCY = 25) so a large seed list never floods outbound.
Each crawl fetches <origin>/.well-known/x402 plus the seller's openapi.json
when present, runs every request through the SSRF guard (safeFetch), caps
response sizes, and records the outcome in a rolling 5-entry history per
seller.
Health-aware routing
A buyer routed to a dead seller wastes money. The router takes that seriously:
- Excluded: a seller whose last
HEALTH_WINDOW(5) crawl outcomes include any errors is not routable and is skipped by/api/route. - Brand new: sellers with no history yet are routable - benefit of the doubt for newcomers.
- Ranked: at equal match score, healthier sellers rank first. Then cheaper wins.
- Snapshot:
GET /api/indexexposes every seller'shealth(0..1),routableflag, and rollinghistoryso an operator can audit the decisions.
The unit tests for these guarantees live in scripts/test-router-health.js
(eight scenarios, offline - they seed the in-memory cache directly via a test
escape hatch).
Trust evidence for one seller (GET /api/x402/seller-trust, $0.005)
The router's gate, exposed as a tool so a buyer can ask about a specific origin before routing to it. Pass a seller origin and get back the evidence the router itself uses: whether the origin is indexed, whether its manifest parses, how many tools it publishes, which chains it actually advertises, how many settled calls it has been observed receiving on-chain, and the verdict on whether the Smart Order Router would spend buyer money there. The gate is returned field by field, so a refusal is explainable rather than a shrug.
It never fetches the seller at call time. This is accumulated crawl and settlement evidence, not a liveness probe, so it cannot be used to make us generate traffic against a third party.
Calling the router
# Default - include everything (local + crawled remotes), pick the cheapest healthy match
curl -X POST https://agent402.tools/api/route \
-H 'content-type: application/json' \
-d '{"query":"ocr image to text","top":5}'
# Neutral discovery: rank only OTHER x402 sellers (exclude Agent402 itself)
curl -X POST https://agent402.tools/api/route \
-H 'content-type: application/json' \
-d '{"query":"ocr image to text","top":5,"include":"external"}'
# Local-only escape hatch (Agent402's catalog only)
curl -X POST https://agent402.tools/api/route \
-H 'content-type: application/json' \
-d '{"query":"ocr image to text","top":5,"include":"local"}'
Returns an object, not a bare array. The matches are in results:
{
"query": "screenshot webpage",
"include": "all",
"count": 2,
"sellers": 2,
"results": [
{
"seller": "https://seller.example",
"sellerHome": "https://seller.example",
"sellerName": "seller.example",
"slug": "screenshot",
"name": "Screenshot any URL through headless Chromium",
"method": "POST",
"route": "/shot",
"url": "https://seller.example/shot",
"price": 0.005,
"priceUsd": 0.005,
"category": "other",
"description": "…",
"score": 13,
"health": 1,
"networks": ["eip155:8453"],
"executeVia": {
"tool": "route-execute",
"price": "$0.01",
"underlyingPriceUsd": 0.005,
"routingFeeUsd": 0.005
},
"untrustedContent": true,
"source": "https://seller.example"
}
]
}
selleris"self"for the local catalog, or the origin URL for a remote seller, so a buyer can address the right seller directly.urlis the full callable endpoint.priceis whatever the seller published (a number or a string);priceUsdis the normalized number to compare on.executeVianames the cheapest execution rung that covers this result (see the ladder above).countis the number of results returned;sellersis how many distinct sellers they came from.- The response echoes back the resolved
includevalue (invalid values fall back toall).
Why this matters - the router as the x402 front door
- Neutral discovery layer.
include:"external"lets buyers explicitly route to non-Agent402 sellers. We list because we trust the ranking, not because we'd rig it for ourselves - and that makes the same endpoint usable as a public discovery API for the whole protocol, not just our catalog. - One integration, the whole ecosystem. A buyer that integrates Agent402's
agent402-clientSDK or the hosted/mcpconnector already has access to 500+ local tools and can route across every other x402 seller without per-seller wiring. - Discoverability that compounds. Sellers don't have to register with Agent402 - appearing in any public x402 registry is enough. The Index pulls them in automatically.
- Trust signals are checkable. Health scores are derived from real crawl outcomes, not self-reports. The full
historyis in/api/indexfor anyone to verify. Agent402 advertises this surface in its own/.well-known/x402manifest under thediscoveryfield so other indexes and agents can find the router programmatically.
Related
- Architecture - where the indexer sits in the request flow
- Operations - 3-rail attribution (USDC / PoW / Heartbeat) on the operator dashboard
- x402-Leaderboard - on-chain ranking using the same Bazaar walk
/api/find- local-only resolver (older, simpler)