๐Ÿ“… Historical page. This content reflects minia2a as of its publication date and is kept for the record. Current model: x402 pay-per-call in USDC on Base only, 5 free trial calls per signed wallet, no credits and no top-up rail. โ†’ See current

The Catalog Is a Snapshot

September 29, 2026

A discovery catalog is a phone book, not a price list. That distinction is easy to state and easy to forget, because the record a catalog holds looks exactly like the record an endpoint serves: scheme, network, asset, amount, payee. Same field names, same shape. One of them is a promise. The other is a photograph of a promise, taken the last time somebody paid.

We swept the whole Bazaar catalog on the CDP facilitator โ€” 19,126 resources โ€” and pulled out every listing whose resource URL is on our host. Then, for each one, we read the 402 challenge that URL serves right now and compared the two.

154 listings · 20 stale
134 listings matched the live challenge byte for byte. 20 did not โ€” all of them understating the price. Zero had a drifted payee address.

Sorted by how wrong they were:

servicecatalog saysendpoint chargesfactor
integration-audit$0.01$50.005000×
ai-audit$0.01$20.002000×
payment-audit$0.01$5.00500×
content-studio$0.01$3.00300×
x402-payment-audit$0.05$5.00100×

An agent that picks a service by browsing the catalog and budgeting from what it reads there is told that a code audit costs a penny. It costs fifty dollars. The endpoint is not lying โ€” the endpoint is the authority. The catalog row is from before the price changed.

Where the row comes from

This is not a crawling bug, and it is not stale-cache. It is the design, and it is in the extension spec. The bazaar extension is how a resource server declares its endpoint specification so a facilitator can catalog it. The facilitator does not fetch your endpoint to see what it costs. It reads the payment payload the buyer sent at settle time:

When a facilitator receives a PaymentPayload containing the
`bazaar` extension, it should:
  1. Validate the `info` field against the provided `schema`
  2. Extract the discovery information

So a listing is not created by a crawl. A listing is created by a payment. Which gives you the property that makes this finding interesting rather than merely annoying:

The refresh trigger is a payment. An endpoint nobody buys is never refreshed. So the entries that stay wrong are precisely the ones with no traffic โ€” and, as the table above shows, the ones with no traffic are where the price is largest and a wrong number costs the reader the most.

The timestamps say the same thing. Of the 154 listings, 89 carry a lastUpdated from the previous day, and the endpoints we serve had 93 paid calls in the trailing week. The freshness of the catalog is a restatement of the payment history โ€” nothing more. The 20 stale rows have been untouched for 19 to 26 days.

What this changes for a buyer

Nothing about how you pay, and everything about where you look. The only authoritative price is the 402 challenge the endpoint returns when you ask for the resource. That is not a style preference โ€” it is how the client flow already works. The catalog gives you a candidate URL; you fetch it, get a 402, and build the payment from the requirement in that challenge. If you shortcut that and construct a payment from the catalog row, you will sign for a penny against an endpoint that expects fifty dollars, and settlement will fail in a way whose error text points at your signature rather than at your source.

Practical version: treat catalog prices as ranking hints, and treat any number you put in front of a human as something you re-read from the live challenge first.

What this changes for a seller

Two things worth checking in your own 402, both found while measuring this. Neither breaks payments โ€” the extension validates cleanly in both cases โ€” and both quietly cost you discoverability.

One: the route template has a grammar, and a plausible value fails it. We were emitting routeTemplate as our service id. The spec wants a URL path template that starts with a slash, and the official SDK enforces exactly that:

const ROUTE_TEMPLATE_REGEX = /^\/[a-zA-Z0-9_\/:.\-~%]+$/;

isValidRouteTemplate("x402-mime-type")   // false, measured
                                        // against @x402/extensions 2.28.0

The facilitator does not reject the record โ€” it discards the field and falls back to the concrete URL. Which is fine for a static route, and it is also why the spec says the field must be absent for static routes. If your routes are static, you are sending a value that can only ever be thrown away.

Two: the resource object carries more than url, description and mimeType. The spec reserves serviceName, tags and iconUrl "to enrich Bazaar search results with a human-readable name, topical tags, and an icon, without any out-of-band admin step". They are optional, and they are the difference between appearing in a search result as a bare URL and appearing as something a human or a model can read and rank. The official client echoes the entire resource object into the payload, so setting them server-side is all it takes.

Both of these are the same failure mode as the stale price, viewed from the other end: a machine-readable surface that is being fed correctly-looking data that no one is checking against the contract. If you emit a bazaar extension, it is worth putting it through the SDK's own validators once โ€” validateDiscoveryExtension and validateDiscoveryExtensionSpec โ€” and comparing the result to what you believed you were sending. Ours passed both, which is exactly why the two fields above had gone unnoticed.

Reproduce it

The measurement is a script, not a paragraph: sweep the discovery index, keep the entries on your host, fetch each URL's live 402, compare the amount and the payee. We run it as a measurement and not a guard, because there is no edit to make โ€” we do not own the catalog row, the spec does not require the snapshot to stay current, and the only thing that refreshes it is a payment, which we cannot self-issue without writing a fake paid row into our own ledger. So it prints the discrepancy and leaves the action with the reader.

Which is the honest place to leave it. A catalog that says $0.01 for a $50 audit is not a bug in the catalog. It is a bug in anyone's assumption that a discovery index is a source of truth about price.