A marketplace only measures what it decided to measure. Our delivery ledger records failures. It does not record "this request returned 200 OK, held the single-threaded sidecar for five seconds, and produced 80 MB". That second category is the one that hurts, and it is invisible in every error-rate dashboard.
This is a write-up of two audits we ran on our own catalogue: one over our handlers, one over the third-party listings we carry. We are publishing the method and the numbers, including the part where our own probing was wrong.
We scanned every handler for allocation sites whose size comes from the caller. Three stood out, all before fixes:
A loan amortisation endpoint answered a single request with 80,763,962 bytes in 5.33 s.
A depreciation endpoint answered with 10.9 MB.
A lorem-ipsum endpoint answered with 6.6 MB.
None of these is an error. All three return 200. They are the shape that makes a service look healthy while it is being used as a memory amplifier — and on a single-threaded sidecar, one such call delays everyone else behind it.
.repeat(), Array.from and new Array. It missed for loops entirely. Adding loops to the population took the scan from 25 handlers to 81, which is how the three above were found at all. The guard was not wrong; it was answering a narrower question than we thought.
After the fixes we re-ran the scan over all 1,712 handlers and libraries, then probed 140 candidates live — extreme value into each handler's first declared input field, measuring response bytes and wall-clock time. One endpoint was flagged:
x402-perp-funding-radar asset=1000000 code=200 bytes=2,098 t=1.938s
That one is not amplification. It is slow because it actually queries four different venues. Both numbers are honest: two kilobytes, two seconds.
Result: zero unbounded allocation sites. We should state the limits of that claim. We only used the first declared field, and we probed 140 of 174 candidates. Multi-field amplification (rows times columns, for example) was not covered.
We carried 171 active third-party listings at the time of the audit. For each one we sent a single unauthenticated request and sorted the outcome into four buckets. An endpoint answering 402 is a correct x402 endpoint and is not counted as a defect.
The catalogue names one host, the endpoint serves another. None found.
None found.
Answered 2xx without payment. Partly our fault — see below.
Timeout, 5xx, or unreachable.
Our first pass used GET for every endpoint. An endpoint that requires POST will happily answer 200 to a GET — that is its routing behaviour, not its price. Re-probing with a payment-shaped POST moved one of the ten to 402, which means it was a correct x402 endpoint all along.
At least one really does answer everyone without a payment gate, on both methods. But the honest summary is: a 200 from a GET probe is not evidence that a service is free. It is evidence that the route exists.
Twenty-nine dead listings sounds like a catalogue falling apart. Grouped by operator, they are not: one operator publishes six subdomains under a single domain, all answering 503, plus one unrelated host returning 530. That is one abandoned deployment, not twenty-nine independent failures.
A per-listing view hides exactly this. If you count dead entries without grouping them by who operates them, you will overestimate how sick your catalogue is — and you will under-estimate how much one abandoned operator costs you.
We have not changed any third-party listing as a result of this audit. The dead ones are recorded, and delisting a batch of them is a decision we want to make slowly rather than in one sweep.
← All posts