2026-11-09 ยท x402 operations
A buyer finds your endpoint in a discovery index. The index shows a price, a payee address, a chain. Where did those three values come from, and how old are they?
For x402scan, the answer is specific: at registration. The self-serve
register call takes a URL, the index fetches your 402 once, parses the
accepts[] array, and stores it. Nothing re-probes that row afterwards. Your solution
can change price tomorrow and the index keeps advertising yesterday's number until you register
the same URL again.
We measured how much that matters for us. On 2026-11-09 we read the x402scan origin page for our
account, took every row on minia2a.uk, and probed each URL against the live challenge
to compare four things: the atomic amount, the payee, the rail (network + asset), and whether the
URL still answers 402 at all.
| measurement | value |
|---|---|
| rows under minia2a.uk | 1,123 |
| snapshot age, median | 0 days |
| snapshot age, max | 65 days |
| rows older than 30 days | 113 |
| rows older than 45 days | 70 |
| accepts per row | 1 (all 1,123) |
Two things stand out. First, the median age is zero โ most of our rows were registered recently, which is what a healthy self-serve flow looks like. Second, the tail is long: 113 rows were snapshotted more than a month ago, 70 more than six weeks ago, the oldest 65 days. Any of those could have been advertising a price we no longer charge, and nothing in the index would say so.
| verdict | rows |
|---|---|
| identical to the live 402 | 1,122 |
| price or payee drift | 0 |
| dead (no 402, no explanation) | 0 |
| retired (404 that names a replacement) | 1 |
| transient / unusable | 0 |
Not one row was wrong. That is not luck: our 402 is computed per request from the live database, so the challenge a probe sees today is the challenge a buyer pays today, and our prices have not moved since those rows were registered. The stale snapshots are stale in date only โ the values inside them still agree with the server.
That is exactly the shape of a latent bug. The mechanism that would publish a wrong price exists; it simply has not fired yet, because nobody changed a price. The next price change makes 113 rows lie simultaneously, and the only notice will be a buyer complaining about a charge it did not expect.
https://minia2a.uk/x402/language-detect returns 404. We delisted that service
because it answered "english" for every Latin-script language โ French, German,
Spanish, Italian and Portuguese all came back English after a confident-looking detection pass.
A 404 is a fine answer to a probe. It is a terrible answer to a buyer, because the buyer arrived from a listing that promised a service. So the 404 body carries the explanation and the replacement:
{
"delisted": true,
"error": "Service not found",
"id": "language-detect",
"hint": "language-detect delisted โ it answered \"english\" for every
Latin-script language ... Use x402-ai-language, which is correct
for those and also returns the script."
}
Whoever follows that dead row lands on a live endpoint that does the job. That is the difference between a dead link and a redirect: the row stays dead, but the journey does not. If you delist a listed endpoint and your 404 is a bare page, you have converted an index into a maze.
x402scan writes rows through two paths, and they land in different buckets:
register writes method: ""openapi.json) writes method: "GET" or "POST"The registration check is keyed on (url, method). So asking "is this URL registered?" with one method and calling it a day under-reports โ and under-reporting is the direction that makes you register everything a second time.
Our own measurements of the same account, on the same day, across three methods:
| query | registered |
|---|---|
| GET only | 29 of 105 |
| GET + POST | 16 of 132 |
| GET + POST + "" | 89 of 132 |
The first two numbers were both wrong, and the second was lower than the first despite
asking about more endpoints โ a bigger population with an incomplete union. Today, over our 144
demand-backed endpoints (286 candidate URLs), the union is complete: 144 registered, 0 missing, with
the buckets at ""=132, GET=94, POST=94.
1,123 of our 1,856 catalog services are in that index; 733 are not. Some of the 733 have never taken a single call. We did not mass-register them. An index gets less useful every time it gains a row nobody searches for, and a provider that floods one to raise a number is optimizing the provider's graph, not the buyer's. Discovery is a shared resource; spam spends it.
lastUpdated is when the row was registered, not when it was last verified. A fresh
date is not evidence the row was re-read.A listing is a photograph of your 402 taken by someone else. The 402 is the contract. If you cannot make the index notice that the contract changed, you owe it โ and your buyers โ a scheduled re-read of the photograph.
The practical version, for anyone running a pay-per-call endpoint: register the same URL again to force a re-probe, do it after any price or payee change, and audit the rows you already have on the same schedule you audit your own documentation. And make sure a dead row points somewhere alive โ the index will keep the dead link for as long as it likes.