1. readfetch
  2. docs

API reference

One header pays for the call

Every endpoint is a POST to https://readfetch.com with a JSON body. No account, no API key: the first call comes back 402 with the price, you sign it, and the retry runs. Here is the whole contract — payment, errors, billing and the endpoint table.

Base URL

One host, one content type, no auth header.

Every call is POST https://readfetch.com/v1/… with content-type: application/json. The body is JSON and so is the response, including for origin 4xx and 5xx on /v1/fetch. A request body over 1 MiB is rejected before any work starts.

Discovery documents — /openapi.json, /llms.txt, /SKILL.md — are plain GETs and cost nothing. /v1/probe is the only free POST.

Payment

x402: the payment is the credential.

Call the endpoint without payment. It answers 402 with the quote, both in the body and in a base64 PAYMENT-REQUIRED header:

402 body
{
  "error": "payment required",
  "price_usd": "…",
  "pay_to": "0x26C2308aDC8fe5FFE6d4183588DD2DD22ed86071",
  "network": "…",
  "asset": "USDC",
  "accepts": [
    {
      "scheme": "exact",
      "network": "…",
      "amount": "…",
      "maxAmountRequired": "…",
      "asset": "…",
      "payTo": "0x26C2308aDC8fe5FFE6d4183588DD2DD22ed86071",
      "resource": "https://readfetch.com/v1/text",
      "mimeType": "application/json"
    }
  ]
}

Only pay_to is fixed here. amount, asset and network come from the quote for that call and that deployment — read them, do not hardcode them. amount is a string of atomic USDC units (six decimals), never a float.

Sign an EIP-3009 TransferWithAuthorization for exactly accepts[0].amount, base64 the x402 payload, and resend the identical call with PAYMENT-SIGNATURE. The retry carries both headers:

the retry

A delivered response carries PAYMENT-RESPONSE (the settlement, with its transaction) and RECEIPT (our signed receipt — keep it, a claim needs it). A nonce can be used once: a replayed payment gets 402 payment already used, so sign a new authorization.

In test mode any non-empty x-payment-proof header passes and nothing settles. That is the mode to build against.

x402 settlement is live on eip155:8453: sign a USDC transfer and retry with PAYMENT-SIGNATURE.

Errors

Every failure is a body you can branch on.

StatusBodyWhat to do
402{ "error": "…", "accepts": [ … ] }Sign accepts[0] and resend with PAYMENT-SIGNATURE.
413"response exceeds N bytes"Lower max_bytes, or read a section instead of the page.
422"error", "hint"Validation refused the request — a private address, a missing field. Nothing ran.
429"error" + Retry-AfterWait the advertised time, then retry the same call. Nothing settled.
502"upstream fetch failed"Retry once; if it repeats, /v1/probe to see what the origin answers.
504"upstream request timed out"The budget ran out. Give a longer timeout_s or ask for less.
200billed: false + hintNot charged. The hint names the next call to try.

A wall, an empty extraction or an unsupported type is a 200 with billed: false, not an error: the origin answered, so the answer is a description of what came back.

Billing

Settled after the work, only on delivery.

The handler runs first and the payment settles after it. billed: false means the rail was never touched — an origin 4xx or 5xx with no content, a binary body, a bot wall, an empty extraction after the browser attempt, or a map with zero URLs.

There is no free daily tier on this instance. Every published endpoint except /v1/probe answers 402 first and is charged on delivery.

Each paid response carries content_sha256 and a provenance block: final URL, origin status, bytes, renderer, parser, extracted characters and whether the text was truncated. That is what makes a wrong answer refutable without trusting us.

Claims

A credit, not a refund — the rail cannot refund.

If a paid call delivered nothing, POST /v1/claims issues a single-use credit when the defect is provable from our own log. The signature is EIP-191 over sha256(receipt || rid), which proves you hold the payer key rather than a leaked receipt.

claim
{
  "receipt": "<RECEIPT header value>",
  "reason": "empty",
  "address": "0xpayer",
  "signature": "0x…",
  "note": "optional, 500 characters"
}

empty and truncated are decided automatically from the record. wrong_content needs a human. A rejection never consumes the receipt: 404 unknown, 409 already claimed, 422 not corroborated, 429 cap reached.

The answer is {"credit": "…", "amount_atomic": "…", "expires_at": "…"}. Present it on the retry as X-CREDIT; it is bound to the payer address, single use, expires after 30 days, and covers one call. PAYMENT-SIGNATURE and X-CREDIT are mutually exclusive on one request. Claims are capped per address per day.

Limits

What is enforced, not what is hoped.

  • Request body: 1 MiB of JSON.
  • Redirects: 5, each hop re-validated as a public address; loopback, private, link-local and metadata addresses are refused.
  • Time: every timeout_s is a whole-call budget that starts at the first DNS lookup. DNS, connect, redirects, every sitemap fetch, every retry and every render share that clock; no step starts a fresh timer. The default and maximum for an endpoint are printed on its page.
  • Nothing that was not delivered is billed, including a budget that expires mid-walk — that returns status: "partial".

Machine-readable

Everything above, in a form an agent can parse.

Endpoints

Generated from the catalog. Each row links to its page.

MethodEndpointPriceReturns
POST /v1/text$0.005Article text and metadata, boilerplate stripped. No browser by default; falls back to one when a page blocks plain HTTP.
POST /v1/map$0.005Sitemaps, robots rules, feeds and link counts for a whole site, in one call. Plan a crawl before paying for it.
POST /v1/scrape$0.005Chromium renders the page, runs interactions, and returns Markdown, text or HTML with its links.
POST /v1/scrape/residential$0.02Scrape through a rotating residential exit, for targets that refuse a datacentre address. Same contract as /v1/scrape, one attempt, priced for the bandwidth.
POST /v1/fetch$0.003Raw HTTP through the same egress. JSON APIs, GraphQL, PDFs, byte ranges - anything that is not a page.
POST /v1/batch$0.0015/pageUp to 50 URLs in one call and one payment. Results in input order; undelivered pages come back as credit.
POST /v1/pdf$0.005/documentDocuments, not pages: page text plus one Markdown body, priced per document rather than per page.
POST /v1/section$0.003Read one heading or one selector instead of the whole page - ten to a hundred times fewer tokens.
POST /v1/diff$0.003Stateless change detection. Send the hash you hold, get back whether it changed and a bounded diff.
POST /v1/screenshot$0.003PNG, JPEG or PDF of a rendered page: viewport, full page, or a single element.
POST /v1/search$0.005Web search without a search subscription, returning titles, URLs and snippets.
POST /v1/crawl$0.003/pageBounded async crawl with a job id, a page cap quoted up front, and per-page Markdown.
POST /v1/probefreeMetadata before spending: status, type, title, size, language, whether a bot wall is waiting. No page body.

Flat prices run $0.003–$0.02 per call; /v1/batch and /v1/crawl are priced per delivered page and quoted before the work starts.