x402 API finder and Base reads
Four paid routes, each settled in USDC on Base through x402 with no account or API key. The finder returns paid x402 APIs for one task that answered a live payment challenge just now. The three Base reads return live public chain data. A call settles only when it produced a result.
Routes and prices
| Route | Price | Settles only when |
|---|---|---|
POST /api/x402/find-apiGET /api/x402/find-api?task= | 0.005 USDC | at least one live match is found |
GET /api/x402/base/balances?address= | 0.002 USDC | the read succeeded |
GET /api/x402/base/tx-check?hash= | 0.002 USDC | the transaction was found and mined |
GET /api/x402/base/gas | 0.0015 USDC | the read succeeded |
An unsigned request always gets the HTTP 402 challenge, whatever its input. A signed request
is validated first, then its payment is verified with the facilitator, then the work runs,
and only then is the payment settled. Bad input returns 400, a payment that does not verify
returns 402, no live match or an unknown transaction returns 404, and an unavailable RPC
returns 503, each with payment_settled: false. A signed HEAD
request is refused with 405 because it has no body to deliver.
A paid result always arrives with its payment. When the facilitator settles, the result
carries payment.txHash and a PAYMENT-RESPONSE header. When the settle call
times out or fails without a clear answer, the transfer may still land, so the result is
delivered anyway with HTTP 200 and payment.status: "unconfirmed", and Ontario
logs it for reconciliation. When the facilitator refuses the settlement outright,
the answer is 402 with stage: "settle" and payment_settled: false,
and no result is delivered.
How the finder works
- Candidates. Ontario keeps a copy of the Coinbase CDP Bazaar catalog with Base USDC listings that settled in the last 30 days. Each result reports
candidate_sourceandindex_as_of. When the copy is missing or older than 12 hours, the live Coinbase CDP Bazaar, Agentic Market and Ontario ready-catalog searches are used instead; when it has too few matches, those searches add their candidates after the index matches. - Skipped before probing. Ontario's own paid routes, templated endpoints, hosts that opted out, and write or money-moving paths for every method, GET included: paths containing send-token, send-native, transfer, withdraw, swap, bridge, execute, invoice, order, mint, deposit or stake, and paths with a word such as pay, payment, payout, purchase, buy, checkout, sell, trade, send, tip, donate, charge, refund, claim, redeem, approve, burn, borrow, lend or repay. A host is compared without a trailing dot.
- Ranking. Task fit, price, and repeat buyers (Bazaar-reported calls beyond unique payers). Marketplace counters are reported by the marketplace and can be inflated or frozen.
- Probing. At most 8 probes per call and 2 per host, 3 s connect and read timeouts, at most 64 KB of a challenge body read, public HTTPS hosts only, no redirects followed. The call waits at most 6 s for all probes together; a probe still running then counts as
probe_timeout. A GET candidate receives the listing's example query; a POST candidate receives the listing's example body. No payment header or signature is attached. An observation is reused for up to 10 minutes, so repeated calls in that window do not probe the same endpoint again. - Kept. Only endpoints that answered with a valid Base USDC x402 challenge within the caller's
max_usdc. A 429 answer counts as not live. Each result carries the live price,price_driftagainst the listing,payto_matches_listingwhen the listing publishes a Base payTo, and a ready-to-call request. Up to 5 results are returned.
Probe User-Agent and opt-out
Finder probes to a provider's endpoint carry this User-Agent:
OntarioProtocol-x402Finder/1.0 (+https://ontarioprotocol.com/docs/x402-api-finder; unpaid probe, no payment attached)
Reads of the Coinbase catalog for Ontario's copy carry OntarioProtocol-x402FinderIndex/1.0 (+https://ontarioprotocol.com/api/x402/find-api; public catalog read);
they go to Coinbase, not to providers.
Opt-out: email hello@ontarioprotocol.com with the host name. A host on the finder's opt-out list, and its subdomains, is neither probed nor returned in results.
Optional Jev re-rank and where task text goes
The re-rank is off by default, and then no task text or candidate data goes to TypeSafe.
Separately, whenever the live search fallback runs, the task text is sent as a search query
to Coinbase CDP Bazaar and Agentic Market. When the operator enables the re-rank, after the probes Ontario sends the task text and up to
5 live candidates' public names, endpoints and descriptions to
TypeSafe (api.typesafe.ai) for a task-fit opinion, with a
4 s deadline and a daily call quota. Task text that looks like
a credential, email address, private key or query-bearing URL is never sent.
Jev can move one live match to rank 1. It cannot add an endpoint or change a price, budget or
network check. If Jev finds no fit and the best live match covers less than
35% of the task terms, the call returns
no match and is not charged. Any Jev failure keeps the deterministic order. Each result states
ranking_method (jev, deterministic or
deterministic_fallback); when Jev was called, jev.disclosure reads:
The task text and the live candidates' public names, endpoints and descriptions were sent to TypeSafe for a task-fit opinion. Jev can reorder live matches; it cannot add an endpoint or change a price, budget or network check.
Base reads
- Parameters. Only the parameters listed here are accepted. An unknown parameter (for example
expectedToortoken), or a single-valued one given twice, is refused with 400 and nothing is charged. - balances:
address(required) andtokens(optional, comma-separated, up to 20 contracts). Returns ETH, plus every token on Ontario's 25-token Base list with a nonzero balance, plus every requested token, with symbol, decimals, raw and formatted amounts and the block read. - tx-check:
hash(required),expected_toandexpected_amountin atomic units (optional). Returns status, block, timestamp, confirmations, ERC-20 Transfers (up to 100 listed;transfers_totalgives the full count) and EIP-3009 authorizations decoded, and match flags for the expected payTo and amount, which cover every USDC transfer. If the RPC has not yet served the block of a brand-new receipt,timestampis null with atimestamp_note. Receipts with 300 or more confirmations are cached for 10 minutes. - gas: no input. Returns the base fee, priority-fee p25/p50/p90 over the last 20 blocks, slow/normal/fast
maxFeePerGasandmaxPriorityFeePerGassuggestions, L1 data-fee upper bounds and total-cost estimates for an ETH and an ERC-20 transfer. - Source. Batched JSON-RPC with a 2 s budget per RPC, failing over across the operator's own Base RPC when one is configured and then
https://mainnet.base.org,https://base-rpc.publicnode.com,https://base.drpc.org. RPC requests carryOntarioProtocol-BaseReads/1.0 (+https://ontarioprotocol.com). Only public chain data is read and nothing beyond the request is stored.
Limits
- The finder checks the HTTP 402 layer only. It never calls a provider's paid response, so a live challenge shows the endpoint is up and payable now, not that its paid answer is correct, safe or fit for the task.
- Results are recommendations, not a safety guarantee. Provider calls are paid separately, under the caller's own spend policy.
- Base reads cover Base mainnet only. Balances carry no prices or USD values, and tokens outside the list and the requested contracts are not discovered.
- Gas values are estimates, not inclusion guarantees.
- Public Base RPCs are rate-limited. When an RPC lags, tx-check can briefly report a new transaction as not found; that call returns 404 and is not charged.
- After the facilitator verified a payment, requests that end without a confirmed settlement (no match, not found, RPC unavailable, settlement refused, or a result delivered while settlement is unconfirmed) are capped at 20 per payer wallet and 3 per payment authorization per 10 minutes; above a cap the route returns 429 and nothing is charged.
Reading the payment challenge
An unsigned request returns the challenge without charging. The decoded PAYMENT-REQUIRED header lists the amount in atomic USDC units, the network, the asset and the payTo:
curl -si "https://ontarioprotocol.com/api/x402/base/gas" \
| awk 'tolower($1)=="payment-required:" {print $2}' | tr -d '\r' \
| base64 --decode | jq '.accepts[0] | {amount, network, asset, payTo}'
A wallet-enabled x402 client pays and retries with PAYMENT-SIGNATURE. With @x402/fetch, a policy can drop any requirement above 0.01 USDC (10000 atomic units) or off Base USDC, so nothing above that cap is signed:
import { wrapFetchWithPayment, x402Client } from "@x402/fetch";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
const USDC_BASE = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
const MAX_ATOMIC = 10_000n; // 0.01 USDC
const client = new x402Client();
registerExactEvmScheme(client, { signer: privateKeyToAccount(process.env.EVM_PRIVATE_KEY) });
client.registerPolicy((_version, requirements) =>
requirements.filter((r) =>
r.network === "eip155:8453" &&
r.asset.toLowerCase() === USDC_BASE &&
BigInt(r.amount) <= MAX_ATOMIC));
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
const response = await fetchWithPayment(
"https://ontarioprotocol.com/api/x402/find-api?task=" + encodeURIComponent("web search for an AI agent"));
console.log(response.status, await response.json());
Machine-readable contracts: buyer OpenAPI, x402 manifest, MCP tools.