Re-check a page against an earlier receipt
Last updated: 2026-10-15
POST /v1/recheck fetches the page named in an earlier receipt again and tells you whether its text changed: unchanged, changed, gone or unreadable, the fields that differ, and a signed receipt naming both the old and the new hashes. Price: US$0.003 per call.
When to use it
- For page change detection without storing anything: keep the receipt, recheck when you need to know.
- Before reusing a quote or a citation you checked days ago.
- To keep a dated chain of evidence that a page stayed the same (each receipt names the one before).
Request
Send exactly one of the two baseline forms. The receipt from an earlier call:
{ "receipt": "eyJhbGciOiJFZERTQSIs…", "return_text": false }
Or a URL and the content_sha256 from our own fetch of it:
{ "url": "https://www.iana.org/help/example-domains", "content_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "return_text": false }
| Field | Required | Meaning |
|---|---|---|
receipt | one of the two forms | compact JWS receipt from an earlier /v1/fetch, /v1/verify/quote, /v1/verify/quotes or /v1/recheck call of this service, paid or trial (not a sample), at most 160 KB |
url | one of the two forms | only without receipt: http or https URL, at most 2,048 characters |
content_sha256 | with url | the page.content_sha256 from our own fetch, 64 hex characters; other tools hash other text, so their hashes will not match |
return_text | no | also return up to 20,000 characters of the new clean text; default false |
quote | no | only with receipt from a verify/quote call in exact or fuzzy mode: the quote exactly as sent to verify/quote. Any other value is a 400 before payment |
quotes | no | only with receipt from a verify/quotes call in exact or fuzzy mode: every quote of that call, exactly as sent, in the same order, 1 to 20. Not with quote. Any other value is a 400 before payment; on the edge it is a 501 not_on_edge, uncharged |
history | no | 1 to 10 earlier receipts of this service for the same URL, oldest first (fetch, quote, quotes or recheck; paid or trial); adds a timeline. Not stored: send it again each time |
The page fetched is the baseline's url (the URL first asked for, not the final URL), so redirects are followed again. The body may be up to 168 KB, so a long receipt fits.
History (timeline)
Send your own earlier receipts for the same URL in history and the answer gains a timeline: each receipt in order, then this check. changed on each entry compares its clean-text hash with the entry before it (null for the first, or when a hash is missing). A receipt that does not verify, names another URL or is out of order is a 400 baseline_invalid before payment, with its index. The timeline is also in the signed recheck receipt. Requests without history are unchanged.
Answer
From the free sample (GET /v1/sample/recheck), shortened:
{
"change": "unchanged",
"changed": false,
"differences": [],
"baseline": {
"source": "caller",
"receipt_sha256": null,
"url": "https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/v1/sample/page",
"content_sha256": "0e429607…"
},
"elapsed_seconds": null,
"page": {
"final_url": "https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/v1/sample/page",
"http_status": 200,
"content_kind": "real",
"content_sha256": "0e429607…",
"raw_sha256": "179b9ac4…"
},
"text": null,
"injection_flags": [],
"advice": "The page's text is the same as in the baseline (byte for byte on the clean text). Markup or other bytes may still have moved; see differences.",
"receipt": "eyJhbGciOiJFZERTQSIs…",
"tier": "paid",
"rail": "x402",
"network": "eip155:8453"
}
change | changed | Meaning |
|---|---|---|
unchanged | false | The page's text is the same as in the baseline. A page already gone in the baseline and still gone is unchanged too. |
changed | true | The page's text differs from the baseline. Fetch it again or check your quote before you rely on it. |
gone | true | The page no longer exists (404 or 410, or the site's name no longer resolves). |
unreadable | null | We could not read the page this time (page.content_kind says why), so we cannot say whether it changed. Try again later. |
- Receipts are signed by
isshttps://still-rapids-9yt7.here.now (keys at /.well-known/jwks.json), not by the origin you called. Check one free withPOST /v1/receipt/verify(docs). differenceslists every compared field that differs, whatever the verdict:content_sha256,raw_sha256,http_status,final_url,content_kind, and with a receipt baseline alsoserver_ipandtls.cert_sha256when both sides have them.baselineis what was compared against. With theurlform,sourceiscallerand the fields only a receipt carries are null.elapsed_secondsis the time since the baseline was fetched (null with theurlform).page,textandinjection_flagsare the new fetch, as [/v1/fetch](/docs/fetch) gives them.
What "changed" means
The comparison is byte for byte on the clean text we extract (content_sha256). Markup-only changes are not changes: raw_sha256 may differ while the answer is unchanged. A page with a date, a counter or rotating text reads as changed on every visit. A content_sha256 from another tool will not match ours: use the hash from our own fetch.
Quote
With quote, the quote is matched on the live page again and the answer adds quote, with a verdict:
quote.verdict | Meaning |
|---|---|
still_exact | The quote was exact in the receipt and is exact now. |
now_exact | The quote was fuzzy or not found in the receipt and is exact now. |
now_fuzzy | The quote matches now only with small differences. |
now_missing | The quote is not on the page now, and the page is real (page.content_kind). |
unchecked | The quote is not found on a page that is not real, so this says nothing about the quote. |
With quotes (a verify/quotes receipt), each quote gets its own verdict in quotes, in the order sent: index, quote_sha256, verdict (the same values as above), was, now, score, start, end, occurrences, and error when that quote was too costly to match now (its verdict is then unchecked). If every quote is too costly, the call is a 422 match_too_costly, not charged.
Chaining
The recheck receipt is itself a valid baseline: send it to the next call. Its result.baseline.receipt_sha256 is the SHA-256 of the receipt you sent, so a chain can be checked offline with verify-receipt.mjs.
Charges and errors
The call is charged like /v1/fetch whenever it answers, gone and unreadable included: the fetch was done and the receipt is evidence of it. If we read no page at all (a timeout, a connect error, a bad decode, too many redirects or a redirect we refuse), the call answers 502 upstream_unreachable with details.reason set, and is not charged. Every baseline problem is a 400 before payment, never charged:
bad_request: neither or both baseline forms, orcontent_sha256withouturl.baseline_invalid: the receipt does not verify against this service's keys, is not a fetch, verify/quote, verify/quotes or recheck receipt, is a sample, or names no URL;details.reasonsays which.
The other errors are those of /v1/fetch; see Errors. On the edge, a PDF page answers 501 not_on_edge, uncharged.
Compared with a hosted watch
No schedule, no webhook, nothing stored: you call when you want to know, and pay US$0.003 per check.