How to get a signed receipt of a web page
Last updated: 2026-10-10
To get a signed receipt of what a web page showed at a given time, have an independent fetcher load it and sign the result. POST /v1/fetch (US$0.002) loads the URL and returns an Ed25519-signed receipt with the final URL, status, time, content hash and a verdict on what came back. Anyone can check it offline.
What the receipt records
The receipt is a compact JWS (alg EdDSA). Its payload includes:
- the URL you asked for and the final URL after redirects, with the HTTP status;
retrieved_at, the time of the fetch;content_sha256, the SHA-256 of the page text, so anyone holding the text can match it;content_kind:real,bot_wall,js_shell,paywall,http_errorand others;- the site's IP address, TLS certificate details and a short list of response headers, where our fetcher could read them.
The full field list is in How it works.
Get one
{ "url": "https://www.iana.org/help/example-domains", "return_text": true }
Send that to POST /v1/fetch. With return_text you also get up to 20,000 characters of clean text. Try the route free first: [GET /v1/sample/fetch](https://vehcdj664efetfrsolne5umanq.srv.us/v1/sample/fetch). Details: Fetch a page. Step by step with curl, from the free sample to matching the page text: How to get a signed receipt of a web page, step by step with curl.
Pay per call with x402
No account or key is needed. A valid request with no payment gets 402 Payment Required with the price in a PAYMENT-REQUIRED header; an x402 v2 client signs that offer and sends the request again. With @x402/fetch (npm install @x402/fetch @x402/evm viem) and a Base Sepolia wallet made only for testing, holding test USDC:
import { wrapFetchWithPayment, x402Client } from '@x402/fetch';
import { registerExactEvmScheme } from '@x402/evm/exact/client';
import { privateKeyToAccount } from 'viem/accounts';
const client = new x402Client();
registerExactEvmScheme(client, { signer: privateKeyToAccount(process.env.ATTESTPAGE_X402_KEY) });
const payingFetch = wrapFetchWithPayment(fetch, client);
const res = await payingFetch('https://vehcdj664efetfrsolne5umanq.srv.us/v1/fetch', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ url: 'https://www.iana.org/help/example-domains', return_text: true }),
});
const answer = await res.json();
console.log(answer.page.content_kind, answer.page.content_sha256, answer.receipt);
The wrapper signs the offer it gets. To check the price and network before signing, see One x402 payment step by step.
Check one
Any receipt can be checked against our published keys with POST /v1/receipt/verify (free), or with no call to us at all:
curl -O https://vehcdj664efetfrsolne5umanq.srv.us/dl/verify-receipt.mjs
node verify-receipt.mjs --selftest
See Verify receipts offline. The keys are at /.well-known/jwks.json.
Receipts for other things
- A quote on a page: Verify a quote signs the match with the page details.
- Many links at once: Check links signs one receipt over up to 10 URLs.
- A file you hold, not a URL: Attest a hash (US$0.002) signs a timestamp over a SHA-256 you send. We never see the file.
Questions
Is this the same as a web archive snapshot?
No. An archive stores the page for others to view. A receipt is a small signed statement about one fetch that you keep yourself; we do not publish it, and pages fetched for you are not stored (Privacy).
Does the receipt show the page is accurate?
No. It records what our fetcher saw at that time. Pages change and can show different content to different visitors.
Why would an agent want one?
To show later what a source said when it was used: when handing work to another agent, answering a dispute about a citation, or keeping an audit trail of the pages a task relied on.