DEV Community

Noboru Miyata
Noboru Miyata

Posted on

Check an x402 Endpoint Before You Pay: One Call, Real 402 Responses

An x402 resource can answer 402 forever and still never deliver. Before you spend anything on one, GET /check?url=<target> answers three questions in a single call: is the target alive right now, what exactly is it asking for (amount, asset, network, payTo, required fields), and what changed since the last check. It costs 0.01 USDC per call on Base mainnet.

Here is the whole integration, and the real responses it returns.

One line to use it

curl -i "https://x402-catalog-history.ai-business-surface.workers.dev/check?url=https://example.com"
Enter fullscreen mode Exit fullscreen mode

First call: the live 402, as measured

Measured on 2026-10-14. The status line is what you act on, and the payment terms come in two places at once:

HTTP/1.1 402 Payment Required
Content-Type: application/json; charset=utf-8
PAYMENT-REQUIRED: eyJ4NDAyVmVyc2lvbiI6MiwiZXJyb3IiOiJwYXltZW50X3JlcXVpcmVkIiwicmVzb3VyY2UiOnsidXJsIjoi…
Enter fullscreen mode Exit fullscreen mode

The v1 JSON body carries the same offer in the older shape:

{
  "x402Version": 1,
  "error": "payment_required",
  "accepts": [
    {
      "scheme": "exact",
      "network": "base",
      "maxAmountRequired": "10000",
      "resource": "https://x402-catalog-history.ai-business-surface.workers.dev/check",
      "mimeType": "application/json",
      "payTo": "0x6f60659390aDA1D02B14024d8c9d0C889f2F89c3",
      "maxTimeoutSeconds": 60
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Decoding the PAYMENT-REQUIRED header (base64 JSON) gives the v2 form:

{
  "x402Version": 2,
  "error": "payment_required",
  "resource": {
    "url": "https://x402-catalog-history.ai-business-surface.workers.dev/check",
    "mimeType": "application/json",
    "serviceName": "x402-check-before-you-pay"
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:8453",
      "amount": "10000",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "payTo": "0x6f60659390aDA1D02B14024d8c9d0C889f2F89c3",
      "maxTimeoutSeconds": 60,
      "extra": { "name": "USD Coin", "version": "2" }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Two details worth wiring in correctly:

  • network is base in the v1 body and eip155:8453 in the v2 header. Same chain, two spellings — a client that reads only one of them will reject the other.
  • amount is "10000" in atomic units of the asset (USDC has 6 decimals), so this is 0.01 USDC.

Second call: the 200 delivery

A purchase on x402 is two calls by design. You send the identical request again, this time with the PAYMENT-SIGNATURE header holding your signed authorization, and you receive the 200 delivery. The JSON that comes back is built from these blocks:

Field What it tells you
target url, host, path of the resource you asked about
checked_at When the probe ran
alive + reason Whether the target answered, and the reason when it did not
probe method, requests_made, http_status, latency_ms, final_url, redirects, content_type, payment_header_sent
payment_challenge The live 402 terms: amount_atomic, amount_usd, asset, asset_symbol, network, pay_to, scheme, max_timeout_seconds, required_fields
response_shape kind and the top-level keys the target actually answered with
history stored_checks, first_checked_at, previous_checked_at, uptime { reached, of, ratio }, challenges_seen, changes_since_previous
delivery_evidence source, first_party, known_deliveries

The service publishes an example delivery inside its own PAYMENT-REQUIRED extension, which is a convenient way to see the full shape before buying:

{
  "product_id": "x402-check-before-you-pay",
  "target": {
    "url": "https://x402-catalog-history.ai-business-surface.workers.dev/history",
    "host": "x402-catalog-history.ai-business-surface.workers.dev",
    "path": "/history"
  },
  "checked_at": "2026-10-14T00:00:00.000Z",
  "alive": true,
  "reason": null,
  "probe": {
    "method": "GET",
    "requests_made": 1,
    "http_status": 402,
    "latency_ms": 143,
    "final_url": "https://x402-catalog-history.ai-business-surface.workers.dev/history",
    "redirects": [],
    "content_type": "application/json; charset=utf-8",
    "payment_header_sent": false
  },
  "payment_challenge": {
    "present": true,
    "source": "PAYMENT-REQUIRED header (base64 JSON)",
    "x402_version": 2,
    "amount_atomic": "10000",
    "amount_usd": 0.01,
    "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "asset_symbol": "USDC",
    "network": "eip155:8453",
    "pay_to": "0x6f60659390aDA1D02B14024d8c9d0C889f2F89c3",
    "scheme": "exact",
    "max_timeout_seconds": 60,
    "required_fields": ["scheme", "network", "amount", "asset", "payTo", "maxTimeoutSeconds", "extra"]
  },
  "response_shape": {
    "kind": "x402_payment_challenge",
    "content_type": "application/json; charset=utf-8",
    "keys": ["accepts", "error", "extensions", "resource", "x402Version"]
  },
  "history": {
    "stored_checks": 2,
    "first_checked_at": "2026-10-13T22:00:00.000Z",
    "previous_checked_at": "2026-10-13T23:00:00.000Z",
    "uptime": { "reached": 2, "of": 2, "ratio": 1 },
    "challenges_seen": 2,
    "changes_since_previous": [],
    "baseline": false
  },
  "delivery_evidence": {
    "source": "this service's own paid-delivery log",
    "first_party": true,
    "known_deliveries": 0
  }
}
Enter fullscreen mode Exit fullscreen mode

Bounds the probe respects

The target is fetched once per paid call, with no payment header, so the 402 you read is the one a buyer faces. Before any payment, the call refuses internal, private and link-local hosts, non-web ports, URLs over 2048 characters, and URLs that embed credentials — including on redirects, up to 3 of them. The probe stops at a 5-second timeout and a 64 KB body cutoff, and the refusal is a 400 that is rejected before any payment is verified.

Price

0.01 USDC per call, Base mainnet, settled one delivery per payment to 0x6f60659390aDA1D02B14024d8c9d0C889f2F89c3. The same address receives every other path on the service, so you only configure one payTo.

When it pays for itself

  • You learn that a target is unreachable before signing a payment for it.
  • The payTo you sign against comes from the resource itself, not from a catalogue that may have moved on.
  • Check the same URL twice and the second delivery returns changes_since_previous instead of a full re-read, so a poll costs one call and a diff.

Free catalogue, including the exact input, output and price of every paid path: https://x402-catalog-history.ai-business-surface.workers.dev/

Top comments (0)