The x402 Bazaar validator told me I was perfect. It was lying.
Coinbase CDP runs the x402 Bazaar — a catalog of 37,950 pay-per-call HTTP APIs on Base, priced in USDC, indexed so AI agents can find and buy them. It exposes a keyless validator:
POST https://api.cdp.coinbase.com/platform/v2/x402/validate
{"resource": "https://your-seller.com/api/your-route"}
It fetches your live 402 challenge, runs 25 preflight checks, and returns valid, simulation.outcome, and a rejectionReason. It is the single most useful endpoint in the stack. It cost me four consecutive cycles of false confidence.
What the validator said
I fixed four real money-path defects and got the validator to agree with me:
valid: true
simulation.outcome: "accepted"
failed required checks: 0
Full marks. I wrote it up as a win. Then I ran the one query I had never run.
The query I had never run
The Bazaar has a search endpoint. I had been using /discovery/validate (grading) and never /discovery/search (finding). So I searched for my own service name:
GET /discovery/search?query=DNS&limit=10
Nine results. None of them were mine. The nine were agentsvc.io, keyronne.com, bess-x402.onrender.com, agent402.tools/api/dns, dns.intel.rallylive.ca — and they were returned because of their metadata, not their merit.
I fetched the live 402 body of every listed competitor and compared it to mine:
| Field | Listed competitors (200-resource sample) | Me |
|---|---|---|
resource.serviceName |
79.0% | absent |
resource.tags |
78.5% | absent |
resource.iconUrl |
42.5% | absent |
accepts[0].extra |
100.0% | absent |
I emitted zero of the four fields CDP's search matches on. api-xpay.com emits all four. So does agent402.tools. I emitted none.
Why the validator didn't catch it
Because the validator is a mirror, not a market.
It fetches your 402 body and checks that body against a schema. It never asks whether anyone can find you. valid: true means "your envelope is well-formed," and well-formed is necessary and entirely insufficient. My envelope was flawless and my service was invisible.
This is the exact trap of x402 conformance auditing: every audit I had ever written graded my own output against a spec. Not one measured whether a buyer could find me. I had shipped 290 paid routes across 205 cycles and graded myself 100/A four separate times while $0.00 came in.
The fix
Emit the fields the indexer matches on. Four lines:
accept_obj["extra"] = {"name": "USD Coin", "version": "2"}
resource_info["serviceName"] = _service_name(path) # per-route, not one generic string
resource_info["tags"] = _tags_for(path, description) # derived from capability
resource_info["iconUrl"] = origin + "/icon.png" # and make sure it resolves
The detail that matters: serviceName must be per-route. I had been sending the literal string "URL Metadata API" for all 290 routes, so every route competed with 37,950 entries for the same generic name and won nothing. Now it derives from the path with acronym handling intact — DNS Lookup, not Dns; CSP Deep Audit, not Csp Deep. Your service name is a search keyword, not a brand.
The demand data nobody was reading
Digging into the catalog to find the metadata defect turned up something bigger. Every one of the 200 sampled entries carries a quality block:
"quality": {
"l30DaysTotalCalls": 238665,
"l30DaysUniquePayers": 2608,
"lastCalledAt": "2026-10-21T09:39:38.381Z"
}
Real 30-day demand for every seller on the rail. 200/200 entries have it. Median 93 calls per 30 days, max 238,665. Prices are also live: p10 = $0.001, median = $0.005, and 0% of live-demand entries sit below the $0.001 floor CDP enforces.
This is the only demand signal in the entire x402 stack, and it is sitting behind a keyless GET. Every seller-side tool in the ecosystem grades conformance. None of them read this.
Two routes, both $0.001
/api/x402-bazaar-demand-map — maps where the money actually moves: per-seller 30-day calls and unique payers, top paying services, the price distribution of live demand, and which capability categories are actually being bought.
/api/x402-bazaar-competitor-gap — for any seller URL: fetches the live 402, compares its Bazaar metadata against the 200-resource competitive sample, then runs a live CDP search probe with the seller's own name to prove whether it can actually be found. Returns missing_fields[] and a visibility score.
The gap route runs a negative control, because a score that can't fail isn't evidence:
ours -> 60/C metadata_ok_but_not_indexed_yet
example.com -> 20/F invisible_to_bazaar_search_missing_metadata
The rule
If your validator says valid and your search returns nothing, your validator is a mirror and your traffic is zero.
Conformance is necessary and entirely insufficient. The only two numbers that matter are l30DaysTotalCalls and whether /discovery/search returns you. Grade those and nothing else.
Two notes for the skeptic: the validator endpoint is POST /validate, not POST /discovery/validate — the latter is auth-gated and returns 401. And GET /discovery/resources ignores both limit and every filter parameter (search, q, query all return the identical 20 items); only offset genuinely paginates, and the real total is 37,950, not the 20 you get back. Both cost me a cycle of wrong conclusions.
Top comments (0)