State of the x402 network, week of 26 Aug – 2 Sep 2026
2026-09-02. We probe every listing about every 22 minutes and, in waves, pay them. Each week we publish what the instrument saw — including what it got wrong. This week it got several things wrong; the counts are below, each with the query or dated snapshot it came from. Figures are from snapshots taken 2026-09-01 and 2026-09-02, not from live pages. CC-BY-4.0. Machine-readable twin: JSON.
1. We marked 64 working endpoints “failing” 512 times. The fault was ours.
Our prober checks DNS before it fetches (an SSRF guard). Until 2026-09-02, a
lookup that errored — the resolver rate-limiting us, SERVFAIL, no
answer — was recorded identically to a lookup that returned no records: as
the endpoint's failure. That verdict was cached per host for an hour. One bad
lookup for a host with ~100 listings failed all of them for the hour; listings on
a failure streak were re-probed every cycle; five failures in ~25 minutes flipped
them to failing; one pass after the cache expired flipped them back.
Wrong and right again inside an hour, so nobody saw it.
Measured from the probe log: eight episodes on six days (2026-08-23 to
2026-09-01), 512 failing transitions on 64 verified listings of
one host (x402.ottoai.services) that was up throughout, about
7,200 probe failures that were ours (1.4% of probes in that window), and the
published 24-hour pass rate understated by about 1.5 points on episode days.
Fixed the same morning: a lookup error is its own label
(dns_lookup_error), counts against nothing, is never cached; a
genuine no-records answer is cached five minutes, not an hour; leaving
failing now takes three consecutive passes, the same evidence as
earning verified; and probe_pass_rate_24h excludes
our own lookup errors, stated in probe_pass_rate_24h_basis.
Correction #14. Historical rows keep their old label:
the code could not tell the two cases apart, so we cannot retroactively either.
Found because 110 listings changed status 488 times in one day and we asked why.
Episodes: days where failure_reason='ssrf_blocked:dns_no_records'
exceeded 250 against a ~300/day baseline of genuinely dead hosts (the 2026-08-11
to 08-19 band is a different regime — dead hosts re-probed too often
before tiered priority shipped — and is excluded). False transitions:
status_changes rows with reason='probe_fail_streak'
preceded within 1h by a dns_no_records probe and followed within 2h
by a pass; 512 of 555 such rows are the one host, and the remainder sit on hosts
with real intermittency and are not claimed.
2. A seller sent a retired credential 310 times and our 401 never said so
One listing: three edit requests on 2026-08-27 (the day its claim token was
rotated by a re-claim), then 310 since 08-31, every one a 401 that said
“did not match”. True and useless. The edit path logged nothing
— our reject log only covered submissions — so the only signal was a
human noticing a rhythm in a log tail. Two earlier updated_at moves
we had read as successful edits were probe status flips.
Now: every edit rejection is logged with its reason and the field names sent;
an unchanged endpoint_url in a re-sent record is ignored rather than
refused; success returns applied and ignored; and if
the token presented is the one a re-claim retired, the 401 says so, with the
date. The seller's first request after the deploy logged
unauthorized:retired_token.
Source: admin_calls (PATCH count per day for the listing)
and submission_rejects after 2026-09-02 07:35 UTC.
3. One listing generated 1,036 payment-address resets. The other 1,700 generated six.
Each listing's payTo is recorded from its 402 challenge; a change resets its
reputation, because that is the shape a hijack takes. One host
(api.melvea.com) served a different address per request from a
small pool: 597 resets in seven days against six for the rest of the
catalogue, all six legitimate one-time rotations. It could never stay
verified: every reset zeroed its probe count.
Guard shipped 2026-09-01: three or more distinct payTos across the last ten
probes marks the listing payto_unstable, skips the hijack branch,
and the detail response tells agents to verify the address in the challenge they
receive immediately before paying. Flagged automatically at four distinct
addresses; zero resets since.
Source: status_changes with
reason='payto_changed' since 2026-08-19; per-probe payTo stored
since migration 0025.
4. 4.4% of listings declare a response MIME type
Of 1,480 listings re-observed since the field shipped, 65 declare
mimeType; 63 say application/json. Null means “not
declared”, not “not yet observed”. Two send an empty string,
stored as null. Correction #10.
5. Header vs body network lists: 186 identical, 36 subset, 2 disagreements
We had posted “39 hosts offer different networks depending on
channel”. That compared raw strings, so ["base"] against
["eip155:8453"] counted as a difference. Recomputed on the same
503-host run of 2026-08-31, 224 readable in both channels: 186 identical after
normalization, 36 where the body is a strict subset of the header, 2 where the
body names something the header omits.
Of the two: aiagentsarchive.com carries desk where a
chain identifier belongs — a malformed value, its own conformance case.
deskcrew.io is real, and a working-group member reproduced it and
corrected our framing: after normalization the header is the strict subset
and the body is the superset (it adds Solana) — the reverse of every host
in his cohort, so “read the header, it's always the safe superset”
is not a rule either. Both raw challenge pairs were re-fetched 2026-09-02,
unchanged, and shared with the working group. And a dual-emission host
explained from the inside that its v1 body is a deliberate shim for a client
library that crashes on v2-only responses: “subset” is not always
“disagreement”.
Getting to 186/36/2 took us two attempts; the first normalization miscounted
39 case artifacts. That made three independent implementations in four days
miscounting from the same gap, and the working group put the cleanest number on
it: leaving bare solana unmapped alone produces 44 false body-only
hosts on one member's set. The argument for a canonical form is not “which
channel wins”; it is that without one, every implementation invents an
alias table and gets it slightly wrong. Correction #12.
6. What agents search for, after removing ourselves
Our demand feed's top ten was the letters a–j (~49 searches each: clients walking the catalogue). The most-searched phrase came 196 times out of 205 from a client that also edited its own listing 193 times — a seller checking its ranking (the same seller as section 2). One caller presented eight IPs in one second, so “distinct clients” was never a count of callers.
Now: minimum query length; self-monitoring sellers excluded and published alongside; IP counts named as IPs; ranking by persistence — a term must be searched on two or more distinct days by non-seller clients. The first entry that survives every artifact class we know: “real-time stock quotes sub-second latency”, 15 distinct days, 24 client IPs. Correction #13; live feed at /state/demand.
7. Wave 6: 776 endpoints, 242 paid, 204 delivered, $2.90 — and 233 purchases refused
Wave 5's biggest failure class was 128 endpoints that took a blind request and returned 400 (nine of them settled; the rest declined the authorization). Wave 6 added a guard: if the 402 quote declares required parameters we cannot supply, do not buy. It refused 233 purchases. Of the 125 relabeled wave-5 400s, wave 6 paid none: 98 refused by the guard, 24 sent blind and rejected unpaid, 3 price drift.
The residue that survives the guard: 64 endpoints returned 400 to a
request that was quoted cleanly and declared nothing unmet. 55 of those
bodies name a required parameter the listing never declared. 52 declined the
authorization — nothing charged. 12 settled the payment and returned
400 anyway. Three of the twelve, all one operator
(wolframalpha.x402.paysponge.com,
rentcast.x402.paysponge.com), were refunded in full within the run:
$0.04 back to the scout wallet, one transfer from the listed payTo and two from
sibling addresses matching the remaining amounts to the cent. That is the first
refund-on-failure behaviour we have observed from any endpoint. The other nine
(agents.x402stock.xyz) kept $0.18 for validation errors. Refunds
will be catalogued as their own outcome (paid_refunded) from the
next wave.
Delivery rate depends on the denominator, so here are three, wave 5 → wave 6:
- delivered / attempted: 38.9% → 26.3% (falls, because the guard refuses what wave 5 bought)
- delivered / paid (settled or settlement-unproven): 81.9% → 84.3%
- delivered / payment-required-and-reached: 44.4% → 51.8% (attempted minus pre-payment exits: guard refusals, free endpoints, skipped, pre-flight errors, unparseable quotes, price drift; 315 and 394)
The one we would quote: when we pay, 84% deliver. The guard's 233 refusals are their own number, not an adjustment. The census figure on /stats (latest attempt per endpoint across all waves) is a fourth thing and is not comparable to any of these.
Also observed: no multi-payment burst in the log was a retry — every cluster of four or more payments inside 25 seconds is one multi-listing host, distinct listings, distinct transactions, all delivered. Nine of the 785 candidates were never attempted, in the dry run or the paid run; the selection rule that drops them is not yet written down and will be.
Source: the signed wave-6 log (776 rows, one per endpoint, hash-chained
and attested), the wave-5 log (360 rows), and the candidate list (785 rows, 125
tier-1 then 660 tier-2). Paid = rows with a transaction hash plus rows labelled
settlement_unproven. Refunds read from the scout wallet's inbound
USDC transfers on Base, 2026-09-01 21:17–21:40 UTC, matched against
observed.trusted_pay_to.
8. What we are still getting wrong: we only speak GET
Our prober sends an unpaid GET and expects a 402. A POST-only route answers
405 with no challenge, and we record that as a failure. At 2026-09-02 07:50 UTC,
31 listings on 10 hosts were marked failing for exactly that;
by 09:00 UTC it was 34 on 11 — it moves with every probe cycle. Three more
listings on three hosts were delisted with a 405 as their last probe. Worse, our
submission warning tells POST-only sellers to “submit a GET-able route
instead” — a probe limitation we had turned into a policy. A host in
the working group asked how many of the ~1,500 are read that way; this is the
answer.
Hosts affected at the 09:00 read: stableenrich.dev (11),
vibesprings.net (7), stableflare.dev (5),
x402.ottoai.services (3), stableupload.dev (2),
x402.aispace.bot, stable-deepline.dev,
music.x402.press,
israel-counterparty-intelligence.vercel.app,
blockrun.ai, agents.allium.so (1 each). None of these
is known to be down. Method-aware probing (on 405, read Allow; if
POST is listed, retry as POST with an empty JSON body; a 402 is alive) is the
next change; this count will be re-published when it ships, and the submission
warning will be rewritten.
Query: listings not delisted whose most recent probe returned HTTP 405, grouped by host.
Corrections this week
#10 through #14: five in seven days, every one about our own instrument. The alternative was to keep the numbers. All fourteen, with what changed and when: /methodology. Per-endpoint outcomes: /stats. If you run one of the endpoints named here and disagree with a result, the disputes route is on /sellers. Follows The ledger.
state of the network · methodology · stats · sellers · integrate · terms · privacy