Dev.to WebDev ๐Ÿ›  Dev ๐Ÿ‘ 0 ๐Ÿ“– 4 min read

I asked 1,000 x402 endpoints to sell me something. 207 never quoted a price.

The Coinbase x402 discovery directory (the "Bazaar") lists 1,000 resources that are supposed to sell something to an agent. I wanted to know what happens when a stranger with no account, no credentials and no reputation

The Coinbase x402 discovery directory (the "Bazaar") lists 1,000 resources that are supposed to
sell something to an agent. I wanted to know what happens when a stranger with no account, no
credentials and no reputation simply asks for one.

One GET per listing. Requirements read from the body and from the base64
PAYMENT-REQUIRED header. No keys, no payments, no retries. Raw aggregates:
x402.ggez.my.id/bazaar-audit.

The numbers

  • 768 of 1,000 answered 402 with machine-readable requirements.
  • 207 never answered 402 at all โ€” 106 ร— 404, 78 ร— 405, 7 ร— 200, 6 ร— 410, 5 ร— 400, 3 ร— 502, 2 ร— 401. (22 more returned 429: that is the scan being throttled, not the endpoint being dead, so they are excluded from every claim here.)
  • 55 of the 768 publish an outputSchema. That is 7.2%. The other 713 leave the buyer guessing what comes back.
  • 64 use a legacy network string โ€” base (63) and solana (1) โ€” instead of CAIP-2 (eip155:8453, solana:5eykt4โ€ฆ). A strict client cannot route those.
  • 6 advertise Base Sepolia (eip155:84532) inside a mainnet directory.
  • 3 answered 402 with no requirements in the body or the header. Unbuyable as listed.

The mistake I made first, because it is the useful part

My first pass read the body only, and reported 212 listings as "402 with empty accepts[]".
That number was wrong, and wrong in the most embarrassing direction: it accused 212 working
services of being broken.

x402 v2 moved the requirements into the PAYMENT-REQUIRED response header, base64-encoded.
When I re-checked those 212 with headers included, 210 of them were fine. They had been
serving correct, payable requirements the whole time. Only 3 listings in the entire directory
genuinely have nothing anywhere.

That is the same class of bug I had just written about in my own paywall: I read the field I
wrote and agreed with myself. A registry that only parses bodies, or a client that only parses
bodies, will call 27% of a working directory dead. Before you trust a scan โ€” mine included โ€”
ask which half of the protocol it read.

Why each defect costs money

404 and 405. The directory is a catalogue of promises. A buyer agent that spends its budget
walking 404s is a buyer agent that stops trying. 184 of these are not even wrong prices, they
are absent endpoints.

Missing outputSchema. An agent cannot decide whether 0.01 USDC is worth spending if it
does not know the shape of the answer. Without a schema the only rational move is to skip the
listing. This is the single largest defect in the directory and the cheapest to fix: one object
in the 402.

Legacy network strings. "base" is not a CAIP-2 identifier. Clients that switch on
eip155:8453 will not match it, will not find a supported rail, and will report "no compatible
payment method" to their operator. The provider sees zero traffic and no error, because the
rejection happened on the client side.

Testnet in a mainnet directory. Six listings accept Sepolia. A mainnet buyer cannot pay
them; a testnet buyer is not who the directory is for.

No requirements at all. Three endpoints return 402 with nothing that says what to pay.
This is worse than a 404, because it looks like a working paywall.

What a listing should contain

{
  "x402Version": 2,
  "accepts": [{
    "scheme": "exact",
    "network": "eip155:8453",
    "maxAmountRequired": "10000",
    "resource": "https://your.service/endpoint",
    "description": "one sentence a stranger can act on",
    "mimeType": "application/json",
    "payTo": "0xโ€ฆ",
    "maxTimeoutSeconds": 300,
    "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "extra": { "name": "USD Coin", "version": "2" },
    "outputSchema": {
      "input": { "type": "object", "properties": {} },
      "output": { "type": "object", "properties": { "price": { "type": "number" } } }
    }
  }]
}

Six fields, all of them checkable by a stranger in one request. The directory has 1,000
listings and 55 of them do the schema part.

Check your own listing in ten seconds

curl -si https://your.service/paid | head -20

Then decode the header and read it as a buyer would:

import base64, json, subprocess
h = subprocess.run(["curl", "-si", "https://your.service/paid"], capture_output=True, text=True).stdout
for line in h.splitlines():
    if line.lower().startswith("payment-required:"):
        req = json.loads(base64.b64decode(line.split(":", 1)[1].strip() + "=="))
        a = req["accepts"][0]
        print(a["network"], a["asset"], a["maxAmountRequired"], "schema:", bool(a.get("outputSchema")))

If that prints base instead of eip155:8453, or schema: False, you are one of the 713.

Method and limits

One GET per URL, no retries, no payment, no credentials, scanned in a single pass on
2026-10-18. A 404 today may be a path that only answers POST, or a service that moved between
the directory snapshot and the scan. A 429 is the scanner, not the service. Nothing here is a
claim about anyone's code โ€” only about what the directory serves to a stranger who arrives with
nothing.

The aggregates are free and will be re-run:
x402.ggez.my.id/bazaar-audit ยท
x402.ggez.my.id/bazaar-audit.json

If your listing is in the 713, the fix is an afternoon and I have written the schema above.

๐Ÿ“ฐ Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes โ€” full credit and traffic to the original publisher.