How to check many quotes against one page in a single call, step by step with curl
Last updated: 2026-10-13
To check up to 20 quotes against the same web page, send the URL and the quotes to POST /v1/verify/quotes (US$0.02 per call). AttestPage fetches the page once, looks for each quote on it, says whether the page loaded as real content, and returns one signed receipt for the whole call.
This guide runs the call with curl and jq against https://vehcdj664efetfrsolne5umanq.srv.us, then shows the same call as an MCP tool. For every field and error, see Verify quotes on one page.
What the check does
- Fetches the page once. Every quote is checked against the same copy, following redirects.
- Matches each quote. Both texts are normalised first.
exactmeans the words are there;fuzzymeans a close match at or abovethreshold(default 0.9);nonemeans neither. With"match": "passages"each quote is a claim and you get the closest passages of the page to judge yourself. - Judges the page.
content_kindisrealwhen the page loaded as content, orbot_wall,js_shell,paywall,http_errorand others when it did not. - Signs a receipt. One Ed25519 receipt covers the page, the options and each verdict. It holds the hash of each quote, not its text.
Price
US$0.02 per call, whether it carries 1 quote or 20, paid in USDC with x402 v2 on Base mainnet (real USDC). No account, key or sign-up. A request with a malformed URL, an empty quote or a page that robots.txt disallows gets an error before payment and is never charged. If every quote is too costly to match, the call is a 422 and is not charged. From 3 quotes on the same page, this costs less than one verify/quote call per quote. All prices: pricing.json.
1. See an answer for free
The free sample checks three quotes on the demo page: one word for word, one close match and one that is not on the page.
curl -s https://vehcdj664efetfrsolne5umanq.srv.us/v1/sample/quotes | jq '.results[] | {match, score}'
{
"match": "exact",
"score": 1
}
{
"match": "fuzzy",
"score": 0.963
}
{
"match": "none",
"score": null
}
2. Read the price from the 402 offer
Step 3 explains the body. A request with a valid body gets the payment offer without being charged. The PAYMENT-REQUIRED header is base64 JSON:
curl -si -X POST https://vehcdj664efetfrsolne5umanq.srv.us/v1/verify/quotes -H 'content-type: application/json' \
-d '{ "url": "https://www.iana.org/help/example-domains", "quotes": [ "These domains may be used as illustrative examples in documents", "without prior coordination with us" ] }' \
| grep -i '^payment-required:' | cut -d' ' -f2 | tr -d '\r' | base64 -d \
| jq '.accepts[0] | {scheme, network, amount, payTo}'
amount is in USDC base units (6 decimals), so 20000 is US$0.02.
3. Check the quotes
Send url and quotes, a list of 1 to 20 strings (at most 1,000 characters each after normalisation). Optional fields: match (fuzzy by default, exact or passages), case_sensitive and threshold. This body checks two quotes on iana.org:
cat > body.json <<'EOF'
{ "url": "https://www.iana.org/help/example-domains",
"quotes": [
"These domains may be used as illustrative examples in documents",
"without prior coordination with us"
] }
EOF
Pay with any x402 v2 client: it reads the offer from step 2, signs it and sends the same request again with a PAYMENT-SIGNATURE header. One x402 payment step by step shows this with plain fetch, and the MCP server does it for you. Save the answer as answer.json.
4. Read the answer
jq '{summary, kind: .page.content_kind, results: [.results[] | {match, score}]}' answer.json
results has one entry per quote, in request order. summary counts exact, fuzzy and none verdicts, and failed for quotes that carry an error instead of a verdict. page.content_kind and advice are for the page as a whole.
match | page.content_kind | What it means |
|---|---|---|
exact or fuzzy | real | The quote, or a close version of it, is on the page. start, end and context show where. |
none | real | The page loaded and the quote is not on it. Check the quote before you cite it. |
none | anything else | We could not read the page (404, bot wall, JavaScript shell), so we cannot say whether the quote is on it. Read advice. |
A quote that is too costly to match fuzzily gets an error with code match_too_costly on that quote only; the rest are still checked. Use "match": "exact", a shorter quote or fewer quotes per call.
5. Keep the receipt
The receipt field is a compact JWS. Check it with the free verify route:
jq '{receipt}' answer.json \
| curl -s -X POST https://vehcdj664efetfrsolne5umanq.srv.us/v1/receipt/verify -H 'content-type: application/json' -d @- | jq .valid
With MCP
The same call is the MCP tool attestpage_verify_quotes, with the body from step 3 as its arguments. To use it from an MCP client with nothing to install, add the remote endpoint:
{
"mcpServers": {
"attestpage": { "type": "http", "url": "https://vehcdj664efetfrsolne5umanq.srv.us/mcp" }
}
}
One call to the remote endpoint with curl. _meta["attestpage/trial"] set to false asks for the payment offer rather than a trial call, so nothing is charged:
curl -s -X POST https://vehcdj664efetfrsolne5umanq.srv.us/mcp -H 'content-type: application/json' -d @- <<'EOF' \
| jq '.result.structuredContent.accepts[0] | {scheme, network, amount}'
{ "jsonrpc": "2.0", "id": 1, "method": "tools/call",
"params": { "name": "attestpage_verify_quotes",
"arguments": { "url": "https://www.iana.org/help/example-domains",
"quotes": [
"These domains may be used as illustrative examples in documents",
"without prior coordination with us"
] },
"_meta": { "attestpage/trial": false } } }
EOF
{
"scheme": "exact",
"network": "eip155:8453",
"amount": "20000"
}
The result has isError: true and the x402 payment requirements in structuredContent, with the same amount as step 2. Sign one of accepts, put the payload in _meta["x402/payment"] and call again: structuredContent is then the same JSON as answer.json.
Questions
How much does it cost to check many quotes on one page?
US$0.02 per call for 1 to 20 quotes. Twenty quotes in one call cost the same as one.
Is the call charged if the page is dead?
A page that is gone or behind a bot wall is a result, and the call is charged. A host name that does not resolve gets 422 host_not_found before payment and is not charged. A request that fails validation, or one where every quote is too costly to match, is not charged. If the page turns out to be disallowed by robots.txt after payment, the call is answered with an error and not charged.
Does none mean the quote is not on the page?
Only when page.content_kind is real. On a 404, bot wall or JavaScript shell we could not read the page, so none says nothing about the quote.