Verify a quote
Last updated: 2026-10-10
POST /v1/verify/quote fetches a page and reports whether a quote appears on it: exact, fuzzy or none, with offsets, surrounding context, the page verdict and a signed receipt. For a paraphrased claim, "match": "passages" returns instead the passages that share most of its words (below). Price: US$0.01 per call.
It checks your exact words, not a reworded version, against the page and returns offsets and page hashes in a signed receipt, all in one US$0.01 call.
Use it before citing a page, to check that the words you attribute to it are on it, and that the page you got is real content rather than a bot wall or an error page.
For 9 or more quotes on one page, verify/quotes costs less: one call checks up to 20.
Request
{
"url": "https://www.iana.org/help/example-domains",
"quote": "These domains may be used as illustrative examples in documents",
"match": "fuzzy",
"case_sensitive": false,
"threshold": 0.9
}
| Field | Required | Meaning |
|---|---|---|
url | yes | http or https URL, at most 2,048 characters |
quote | yes | text to look for, or with passages the claim to find support for; at most 1,000 characters after normalisation |
match | no | fuzzy (default) also accepts small differences; exact needs the same text after normalisation; passages finds the passages that best share a paraphrased claim's words |
case_sensitive | no | default false; must be false with passages |
threshold | no | fuzzy similarity needed, 0.5 to 1, default 0.9 |
Before matching, both the page text and the quote are normalised: Unicode NFKC, curly quotes and dashes become plain ones, invisible characters are dropped, and all whitespace becomes one space.
Response
From the free sample (GET /v1/sample/quote), shortened:
{
"result": {
"match": "fuzzy",
"score": 0.963,
"start": 211,
"end": 238,
"context": "...the agency said that emissions fell 12 % in 2025, after two years of slow growth...",
"occurrences": 1,
"normalised_sha256": "086a29af…"
},
"page": {
"requested_url": "https://vehcdj664efetfrsolne5umanq.srv.us/v1/sample/page",
"final_url": "https://vehcdj664efetfrsolne5umanq.srv.us/v1/sample/page",
"http_status": 200,
"content_kind": "real",
"signals": ["status_200"],
"title": "Regional climate report 2025 (demo page)",
"content_sha256": "0e429607…",
"retrieved_at": "2026-10-09T14:19:34.903Z"
},
"injection_flags": [],
"advice": "The page loaded and looks like real content.",
"receipt": "eyJhbGciOiJFZERTQSIs…",
"tier": "paid",
"rail": "x402",
"network": "eip155:84532"
}
matchisexact,fuzzyornone.scoreis the similarity from 0 to 1.startandendare code-point offsets into the normalised page text.normalised_sha256is the SHA-256 of that text, so anyone holding the page text can check the offsets.occurrencescounts exact matches.- Read
page.content_kindbefore relying onmatch. Anoneon abot_wallorjs_shellpage means we could not read the page, not that the quote is absent from it. See How it works.
Passages: a paraphrased claim
An agent rarely quotes word for word. With "match": "passages", quote is a claim (for example "The agency reported a 12 percent drop in emissions during 2025") and the answer is the 3 passages of the page that share most of its words:
On the demo page ([https://vehcdj664efetfrsolne5umanq.srv.us/v1/sample/page](https://vehcdj664efetfrsolne5umanq.srv.us/v1/sample/page)), shortened; try it free with [GET /v1/sample/quote?match=passages](https://vehcdj664efetfrsolne5umanq.srv.us/v1/sample/quote?match=passages):
{
"result": {
"match": "passages",
"score": 0.583,
"start": 115,
"end": 271,
"occurrences": 3,
"passages": [
{ "start": 115, "end": 271, "score": 0.583, "text": "It is not a real report and its numbers are made up. In its annual summary the agency said that emissions fell 12 % in 2025, after two years of slow growth.", "missing": ["drop"], "flags": ["negation_mismatch"] },
{ "start": 357, "end": 513, "score": 0.217, "text": "The agency also noted that the share of renewable power rose to 41 percent, and that the next report will cover water use and air quality across the region.", "missing": ["12", "drop", "emissions", "2025"], "flags": ["number_mismatch"] },
{ "start": 0, "end": 114, "score": 0.136, "text": "Regional climate report 2025 This is a demo page served by AttestPage so that agents can try every route for free.", "missing": ["agency", "12", "percent", "drop", "emissions"], "flags": [] }
],
"scorer": "lexical-v2",
"note": "Passages ranked by how many of the claim's words (weighted by rarity on the page) and word pairs they share. This is evidence for you to judge, not a verdict: ..."
}
}
Here "reported" counted as found because the passage has "report". The best passage does support the claim, but only a reader can tell that "fell 12 %" means a 12 percent drop. It is flagged negation_mismatch only because its first sentence says "not a real report": a flag tells you where to look, not that the passage contradicts the claim.
- This is evidence, not a verdict. A score measures shared words, not meaning. "Emissions did not fall" shares as many words as "emissions fell". Read each passage, and check negation, numbers, dates and the words in
missing. - Passages. A passage is a sentence, or two neighbouring ones; text with no sentence ends is cut every 40 words. A sentence ends at
.,!or?before a space, or at。,!or?, but not after a short abbreviation such as "e.g.", "i.e.", "Dr.", "Mr." or an initial ("J. Smith"); "etc." ends one only before a capital. Passages do not overlap and are sorted byscore, highest first. The top-levelscore,start,endandcontextare those of the best passage, andoccurrencescounts the passages. - Score. From 0 to 1: 0.75 × the share of the claim's words the passage holds, each weighted by how rare it is on the page, plus 0.25 × the share of the claim's neighbouring word pairs it holds.
- How words are compared. Without case, with light English stemming ("emissions" meets "emission"), and ignoring common English words such as "the" and "was".
%counts as "percent". In Chinese and Japanese, each character counts as a word. - Offsets and text.
startandendare code-point offsets into the normalised page text, as above.textis cut to 800 characters with...when the passage is longer. - missing. Lists the claim's words that the passage lacks, at most 20.
- flags. Marks a passage worth a closer read; it never changes the score or the order.
negation_mismatch: the claim or the passage has an English negation ("not", "no", "never", "n't", "without" and similar) and the other has none.number_mismatch: the claim has a number the passage lacks and the passage has one the claim lacks, such as 12 against 15 (12 %,12 percentand12.0count as the same number, and so do1,000and1000). An empty list does not mean the passage agrees with the claim. - No match.
matchisnonewith an emptypassageslist when no passage shares a word. - Errors. A claim made only of common words or punctuation gets 400.
- Same answer every time. No language model is used, so the same page and claim give the same answer.
scorernames the method (nowlexical-v2) and changes whenever the same page and claim could give different passages, scores or flags. - Cost. The cost is linear in the page size, so passages never gets
match_too_costly. - Receipt. The receipt signs
mode,scorerand each passage'sstart,endandscore. The text can be recovered from the page at those offsets, andmissingandflagsfrom that text, the claim and thescorer. - Price. The same as any other
verify/quotecall.
A PDF page is matched like any other page. Its text layer is read as described in Fetch: PDF, with no OCR.
- A match on a PDF also has
pdf_pageandpdf_page_end: the 1-based pages where it starts and ends. Each passage has them too. startandendare still offsets into the whole normalised text. In that text, each page break is one space.- Only text inside each page's box is read, as a reader sees it. A quote from text drawn outside the page is not found.
- The receipt signs
pdf_pageandpdf_page_endnext to the offsets.
Only the full service reads PDFs: an edge server answers 501 not_on_edge (details.reason pdf) for one, not charged. Limits are in Limits.
Charging
The quote and options are checked before payment: a bad request gets 400 and is not charged. A quote too costly to match fuzzily gets 422 match_too_costly, not charged; use "match": "exact" or a shorter quote. A URL whose host name does not resolve gets 422 host_not_found, not charged. Results about the page itself, including 404s, bot walls and timeouts, are charged. See Payments.