If an agent finds your endpoint through a third-party index, the price it reads there is not a quote. It is a copy taken at one moment, by one process, and nothing in either index we are listed in re-reads the endpoint afterwards.
We had measured one half of this before. On 2026-10-19 we counted the rows the CDP Bazaar catalog had dropped: 57 of our 153 listings vanished between two full sweeps, including all twenty that advertised $0.01 against real prices up to $50. That post was about absence. This one is about the rows that stay.
The two catalogs fail the same way but at different points:
402 at registration time. Refresh requires re-registering.Neither is wrong to work that way โ a catalog cannot poll every endpoint on the internet. But it means the row is a snapshot of a single instant, and the instant is chosen by an event that may be months old. If you change a price and nobody pays that endpoint again, the index keeps selling the old one.
We read every row our origin has on x402scan โ 174 of them โ then walked each URL back to our own endpoint and compared the live 402 against the stored row on four axes: atomic amount, payTo, network plus asset, and whether the URL still answers a challenge at all.
| 2026-10-22 | |
|---|---|
| rows on our origin | 174 |
| identical to the live 402 | 160 |
| price or payee drifted | 13 |
| URL answers 404 | 1 |
| median row age | 34 days |
| rows older than 30 days | 130 |
The thirteen were not random. Ten rows advertised 1000 atomic units ($0.001) for endpoints we charge 10000 for โ a ten-fold understatement left from a September price change. One went the other way: polymarket advertised $0.50 against a live $0.05, which is worse for a seller than being too cheap, because it persuades a buyer not to look.
And one was the case worth fixing first: a row filed on 2026-07-10 for recall still carried a payTo from our old platform wallet, at a fiftieth of the current price. An index row is what a buyer reads before signing. A stale price overcharges or undercharges on the right chain; a stale payTo sends the money somewhere else entirely.
x402scan's registration endpoint takes a URL and probes it on the spot. We did not know whether a second registration would update the existing row or add a pile of duplicates, so we did one and looked:
# request
POST /api/trpc/public.resources.register {"json":{"url":"โฆ/x402/recall"}}
# response, reshaped for display to the fields that answer the question
success true
id b929311c-โฆ (the July row โ not a new one)
lastUpdated 2026-10-22T06:14:19Z
amount 500000 (was 10000)
payTo 0xab62452bโฆ (was 0xf16f0882โฆ)
network base
The id came back unchanged โ b929311c is the July row โ with a new timestamp, the current price, and the current payee. Registration is keyed by (url, method), and for the "" bucket it updates in place. Ten registrations, ten successes, and the drift count fell from 13 to 4.
Four rows we could not fix from our side. Two endpoints, account-age and math, have extra GET and POST rows that x402scan wrote by crawling our openapi.json on 2026-09-04. Registration only writes the "" bucket, and there is no public route that overwrites a crawled row. Those four stay wrong until that crawler runs again. The one 404 is language-detect, a retired endpoint: we left that row alone, because our 404 body names the retirement and the replacement, so a buyer who follows the dead row still lands on the answer.
Two of the checks in this measurement exist because the first version of the measurement was wrong, and both were wrong in ways that would have produced a confident, useless report.
Comparing payTo as a raw string marked every one of the 174 rows as drifted. The index stores lowercase; our challenge answers an EIP-55 checksummed address. The case is presentation, not identity.
The second was quieter. Our display helper formats atomic units to two decimals. Our cheapest rows are 1000 atomic against a live 10000 โ so a ten-fold drift printed as $0.00 -> $0.01. The verdict survived and the ratio died; a reader would have seen a one-cent rounding difference where the real story was a factor of ten. Sub-cent values now print four decimals next to the raw atomic number.
Both mistakes point the same way: a measurement tool is a claim about the world, and it needs its own negative controls before you believe its red. Ours now has twenty-five, and two of them are those exact mistakes.
Treat any catalog price as a hint and the live 402 as the truth:
402 you get from the resource is the only statement the seller is making right now.One origin, one day. It says nothing about other sellers' rows. Each row was judged on accepts[0] only โ which loses nothing in this population, because all 177 rows we read carried exactly one accept, and the tool prints that arity so the assumption is visible rather than buried. And the index can re-crawl at any time: the four rows we cannot refresh are a statement about this week, not a permanent property.
The general lesson is not that catalogs are unreliable. It is that a discovery index records an event, and events have timestamps. If a price matters to your decision โ it does โ read it from the endpoint.
Measured 2026-10-22 against the live 402 of 177 listed URLs and two full index reads. The reading and its 25 controls: tools/x402scan-listing-parity.py. The earlier catalog measurements: The Catalog Deleted Our Listings.