Agent Research Tools — pay-per-call x402 APIs (USDC on Base, Polygon, Arbitrum, Solana, Avalanche, Sei)
Live: https://x402-seller-pmlm.onrender.com · Agent guide: /llms.txt · OpenAPI: /openapi.json · Discovery: /.well-known/x402
No API key, no signup. AI agents pay per request in USDC on Base (eip155:8453), Polygon (eip155:137),
Arbitrum (eip155:42161), Solana mainnet (CDP facilitator) or Avalanche (eip155:43114) and Sei (eip155:1329)
(free PayAI facilitator) using the
x402 protocol, settled through the Coinbase CDP facilitator and listed in the CDP x402 Bazaar.
Never charged for errors: 400 bad input, 422 nothing found / target unreachable, 503 unavailable.
The x402 middleware only settles a payment when the handler returns 2xx.
Call it
Run it yourself
Node 22+, a CDP API key (https://portal.cdp.coinbase.com), and a Base receive address.
Free routes: /health, /stats, /examples, /llms.txt, /openapi.json, /.well-known/x402, /robots.txt, /icon.png (/icon.svg).
Answer permalinks (/a/<id>)
Every paid /report answer gets a free shareable page at /a/<id> (clean HTML; ?format=json for the stored JSON) with a footer link back to the service. Stored in memory only (max 500, 30 days), so links reset on each Render deploy/restart. Only the answer is stored (question, answer, sources), never IP, user agent or payer. /a/example-mcp is a pinned demo. The paid response leads with bot-friendly fields: answer, answer_citations, key_points[{text,citations}], sources with publisher, published, quote, confidence + confidence_why, checked_at, permalink; the original fields (summary, bullets, ...) are unchanged.
Interest tracker (/stats)
Open /stats in a browser (or /stats?view=simple) for a plain-English page: one headline sentence, a per-tool table (looked at price / paid / bots-crawlers), Pacific times. Programs get JSON (default, or ?format=json). Payments signed by our own test wallet (SELF_PAYERS, default 0x4862…4940) and the unpaid probe just before them from the same visitor are tagged self and excluded from buyer counts (selfPaid shows them).
Per paid route and per hour / UTC day: unpaid 402s, paid 200s, settle failures, rejected payments, uncharged
errors and unique visitors, split by visitor class: client (anyone who sends a payment, or a non-bot user agent such
as node, python, curl, a browser), crawler (CDP Bazaar, x402scan, 402index, other x402 directories), pinger
(cron-job.org, UptimeRobot, ...), bot (search/AI crawlers, scanners, empty UA), head (HEAD/OPTIONS) and self
(scripts/rank-check.mjs). Visitors are sha256(random daily salt + IP + UA); raw IPs are never stored and the salt
never leaves memory. /health carries a compact traffic block; /stats has today, yesterday, last 7 days, 48 hourly
and 35 daily buckets, top user agents and definitions. Memory use is a few hundred KB.
Discovery and ranking
- Every paid route declares
extensions.bazaar (input schema + example, typed output schema + example) and
resource.serviceName / tags / iconUrl / mimeType, per the x402 Bazaar spec.
- CDP Bazaar indexes a route after its first CDP-settled payment and refreshes ranking every ~6 h
(30-day calls, unique payers, metadata quality, availability). Routes with no settlement for 30 days drop out.
node scripts/rank-check.mjs prints the recurring checklist (live status, Bazaar listing + rank per query with and
without the Base filter, CDP validate, on-chain sales, /stats interest tracker, x402scan presence).
- What moves Bazaar search order (CDP docs + observed rankings): relevance of
serviceName, tags and description to
the query (hybrid text + semantic search), blended with 30-day unique buyers, settled calls, recency and metadata
completeness; recomputed about every 6 h; results are capped per domain; indexed metadata only refreshes after a new
CDP-settled payment.
Guard rails
- Settlements are logged to
payments.jsonl (gitignored) only after the facilitator confirms, with payer + tx hash.
- Circuit breaker: 3 failed settlements (verified payment, settle failed) in 10 minutes -> 503 (uncharged) for paid calls.
/news sources: the GDELT GKG 15-minute files (open data, cite gdeltproject.org; indexed in memory so no API rate
limits), Hacker News (Algolia API) and ~26 publisher RSS/Atom feeds (cached 10 min). Only headlines, links, outlet and
time are returned, never article text.
/price reads Uniswap v3 pool state (slot0, reserves) straight from public RPC nodes via Multicall: no third-party
price API, so every number is verifiable onchain at the returned block. Pools under $25k quote-side depth are ignored;
no qualifying pool -> 422 (uncharged). 24h change comes from the deepest pool's own TWAP oracle (observe) or an
archive slot0 read ~24 h of blocks back. Cached 30 s.
/solana-price (and /price for Solana) derives Orca Whirlpool, Raydium CLMM, PumpSwap and pump.fun bonding-curve
accounts by PDA and reads them from public Solana RPC (no indexer, no API key). Not covered: Raydium AMM v4, Meteora.
No 24h change on Solana (public RPC keeps no price history). Under $5k depth -> 422; under $25k -> thinLiquidity.
/balance, /tx, /gas read public RPC nodes only (Multicall3 on EVM). Balances cover native + a curated list of
major tokens per chain plus any tokens= you pass (public RPC does not index every token a wallet holds).
/read and /check only fetch public https hosts (private/loopback/link-local IPs blocked, redirects re-checked, 3 MB / 15 s caps).
MIT-style: use freely. No secrets in this repo; never commit .env.