x402 Fast PDF Parser

June 11, 2026 · View on GitHub

Pay-per-use PDF text extraction sold over the x402 payment protocol. Agents pay $0.002 USDC per document on Base mainnet and get structured text back. Speed is the product: every response carries parse_ms, and the target is sub-second p95 for born-digital PDFs under 5MB.

Live endpoint: https://x402-parser-edge-mainnet.epicblubber.workers.dev — discoverable via the x402 Bazaar (x402scan listing).

Verify it yourself: every parse settles on-chain — watch real settlements arrive at the seller address on BaseScan.

Two deployments, one codebase. Production is [env.mainnet]x402-parser-edge-mainnet: Base mainnet, real USDC, settlement through the Coinbase CDP facilitator, x402 Bazaar discovery metadata in every 402 challenge. The default Worker (x402-parser-edge) is the Base Sepolia testnet deployment, kept as demo/staging — same code, keyless public facilitator, no real money. The NETWORK env var picks the chain; GET /health always tells you which one you're talking to. The seller holds a receiving wallet address only — no wallet keys (mainnet adds CDP API credentials as Worker secrets).

Status

  • Phase 1 — edge seller: Cloudflare Worker, Hono, @x402/hono v2 middleware, LiteParse WASM. Live on Base mainnet with CDP facilitator settlement and x402 Bazaar discovery; Base Sepolia deployment retained as demo/staging.
  • ⬜ Phase 2 — heavy path (Rust Lambda + OCR) for >5MB / ?ocr=true
  • ⬜ Phase 3 — buyer agent (x402 client, doubles as integration test)

Note: the spec named the x402-hono package; that v1 SDK is deprecated upstream (security patches only), so this uses its successor @x402/hono v2. Same protocol, same flow, current header names (PAYMENT-REQUIRED / X-PAYMENT).

API

RoutePriceDescription
POST /parse$0.002 USDCRaw PDF bytes in, JSON text out
GET /healthfreeLiveness + version
GET /freeHuman/agent-readable service description
GET /.well-known/x402.jsonfreeMachine-readable manifest (endpoint, price, network, payTo, schemas)
GET /llms.txtfreePlain-text description for AI crawlers

POST /parse

Request:

  • Content-Type: application/pdf, raw PDF bytes as the body (max 10MB)
  • X-Document-SHA256: <hex sha256 of the bytes> — required
  • X-PAYMENT: <x402 payment> — obtained from the 402 challenge

Success (200):

{ "text": "…full document text…", "pages": 50, "parse_ms": 79, "sha256": "ab34…" }

pages is the page count. parse_ms measures only the parser, so payment and network overhead are visible by subtraction in the bench. Production caveat: Cloudflare Workers freeze in-request clocks during CPU execution (Spectre mitigation; time only advances on I/O), so deployed parse_ms reads ~0. It is accurate under local wrangler dev and will be accurate on the Phase 2 Lambda path; at the edge, total latency is the honest number.

Errors (always JSON, never a raw parser 500):

StatusMeaning
400Payment attached but X-Document-SHA256 missing/invalid, or it doesn't match the bytes
402No/invalid payment — challenge is in the PAYMENT-REQUIRED response header (base64 JSON). Unpaid requests always get 402 first (even empty ones — discovery crawlers probe this way)
409Replayed payment proof (seen in the last 10 minutes)
413Document over 10MB
422Unparseable PDF or parse exceeded the 5s deadline (payment does not settle)
429Wallet over 50 requests/hour, or unpaid request floods throttled per-IP at the edge (100/min)
503Facilitator verification timed out (2s budget) — retry per Retry-After

The mainnet deployment also declares x402 Bazaar discovery metadata (serviceName "Fast PDF Parser", tags data/tools, and a bazaar.info.input example request) in its 402 challenge, so the CDP facilitator can catalog the endpoint for agent discovery.

Request ordering (the paid path)

  1. Per-IP edge rate limit (Workers Rate Limiting API, 100 req/60s), else 429 — unpaid request floods are throttled per-IP at the edge before any other work
  2. Declared Content-Length ≤ 10MB, else 413 — before any payment work
  3. With a payment attached: X-Document-SHA256 well-formed, else 400. Without one, fall through so the 402 challenge answers (crawler probes included)
  4. Payment proof not already consumed, and not bound to a different document (KV, 10-min TTL), else 409 — before burning a facilitator round trip
  5. Payer wallet under 50 req/hour, else 429
  6. x402 middleware: missing payment → 402 challenge; facilitator verify with a 2s deadline → 503 + Retry-After on breach
  7. The verified proof is bound to the document's SHA-256 (the x402 exact payload signs only the transfer, not the payload, so the seller enforces this binding), then parse inside a 5s jail → 422 on malformed input
  8. Settlement happens only after a successful (2xx) parse, so failed requests never charge the buyer; the proof is fully burned on success. A 422 can be retried with the same document, but one proof can't be shopped across different payloads until one parses

Self-hosting: standard Cloudflare Workers deployment; see wrangler.toml for required bindings and secrets.

Paying (until the Phase 3 buyer lands)

Any x402 v2 client works. The bench script is a minimal example using @x402/fetch:

const fetchWithPayment = wrapFetchWithPaymentFromConfig(fetch, {
  schemes: [{ network: "eip155:84532", client: new ExactEvmScheme(account) }],
});

Fund a throwaway wallet with Base Sepolia ETH (gasless transfers — the facilitator settles) and testnet USDC from the Circle faucet.

Benchmark

Total is the full paid exchange (POST → 402 → sign → retry → 200); parse is server-reported parse_ms. The difference is what x402 costs you.

Results (testnet, paid end-to-end) — 2026-06-09, 50 runs/doc, US West client

documentsizeruns oktotal p50total p95total p99parse p50parse p95parse p99
text-2p.pdf0.00MB48/501427ms2095ms2160ms0ms0ms0ms
text-50p.pdf0.05MB48/501676ms2167ms2234ms0ms0ms0ms
text-5mb.pdf4.69MB49/503681ms4124ms4397ms0ms0ms0ms

What the numbers actually say:

  • Parsing is not the cost — x402 is. Server parse time is 6–79ms locally (production parse_ms reads 0 due to the frozen-clock caveat above), while the paid exchange p50 is ~1.4s: a 402 round trip, client-side signing, facilitator verify, and a synchronous on-chain settle before the response returns. The original sub-second p95 target holds for parsing, not for the full paid exchange — closing the gap means async settlement or challenge caching, which is future work, not Phase 1.
  • Big documents pay double transit. The 402 challenge request uploads the full body once before payment, the paid retry uploads it again — that's most of the 5MB doc's extra ~2s, not parse time.
  • Failure accounting: the 2-page doc's two misses were 429s (the smoke test had already spent 2 of that wallet's 50/hour budget — the rate limiter doing its job); the other three were transient facilitator verify failures (~2%, 402 on the paid retry, buyer not charged, no client-side retry in the bench).

Privacy

No document retention: PDF bytes are parsed in memory and never written to storage or logs. We retain only a SHA-256 fingerprint of the document (replay protection, 10-min TTL) and the payer address (rate limiting, 1-hour TTL). Document content is not logged by the application.

Honest limitations

  • The 5s parse deadline can't preempt WASM mid-instruction; it bounds the wait and the Workers CPU limit backstops pathological files.
  • KV rate-limit counters are eventually consistent — the 50/hour limit is approximate.
  • Replay protection covers the KV TTL window (10 min); the on-chain nonce in the exact scheme prevents true double-spends beyond it.
  • The KV replay guard is eventually consistent (~60s) across edge locations: two near-simultaneous requests with the same proof at different PoPs can both reach the parser. Worst case is one wasted parse — never a double-settle, because the EIP-3009 nonce can only settle once on-chain; the loser gets a settlement failure.
  • If settlement fails after a successful parse, the proof is already marked used — the buyer got the goods but the seller may not get paid. Acceptable at $0.002.
  • The payer address used for rate limiting is read from the unverified payment header; forging it only earns the forger a 402.

Repo layout

packages/core/         shared types, limits, hashing
packages/seller-edge/  the Worker (Hono + @x402/hono + LiteParse WASM)
packages/seller-lambda/  (Phase 2, not started)
bench/                 sample-PDF generator + paid latency benchmark