Vercel AI SDK: check several quotes on one page
Last updated: 2026-10-13
A Vercel AI SDK agent that checks up to 20 quotes from one web page in one call to POST /v1/verify/quotes before it cites them. Price: US$0.02 per call, for 1 to 20 quotes. Each quote comes back exact, fuzzy or none, with offsets and context, plus the page's verdict and an Ed25519-signed receipt. Payments are in USDC with x402 v2 on Base mainnet (real USDC).
The tool calls the route over HTTP and pays with @x402/fetch.
To check one quote at a time or a batch of links, on the free trial with no payment code, see Vercel AI SDK: check a quote before citing it. The same check for other frameworks: OpenAI Agents SDK, Claude Agent SDK, LangChain, LlamaIndex and CrewAI.
Install
Node.js 20 or later.
npm init -y && npm pkg set type=module
npm install ai @ai-sdk/anthropic zod @x402/fetch @x402/evm viem
export ANTHROPIC_API_KEY=...
export ATTESTPAGE_X402_KEY=0x...
Any model the AI SDK supports works: install its provider package and pass its model as model. ATTESTPAGE_X402_KEY is the private key (0x and 64 hex digits) of a wallet holding a little USDC. The key stays in your process.
agent-http.mjs
// agent-http.mjs: checks quotes with POST /v1/verify/quotes, paying per call with x402.
import { generateText, isStepCount, tool } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { wrapFetchWithPayment, x402Client } from '@x402/fetch';
import { registerExactEvmScheme } from '@x402/evm/exact/client';
import { privateKeyToAccount } from 'viem/accounts';
import { z } from 'zod';
const ORIGIN = process.env.ATTESTPAGE_URL || 'https://vehcdj664efetfrsolne5umanq.srv.us';
const MAX_UNITS = 20000; // US$0.02 in USDC (6 decimals): the most one call may cost
const client = new x402Client();
registerExactEvmScheme(client, { signer: privateKeyToAccount(process.env.ATTESTPAGE_X402_KEY) });
client.registerPolicy((_version, reqs) => reqs.filter((r) => r.network === 'eip155:8453' && Number(r.amount) <= MAX_UNITS));
const payingFetch = wrapFetchWithPayment(fetch, client);
const verifyQuotes = tool({
description: 'Use before quoting one web page several times: checks 1 to 20 quotes against the page at url. '
+ 'Returns results[i].match (exact, fuzzy or none), page.content_kind and a signed receipt. US$0.02 per call.',
inputSchema: z.object({ url: z.string(), quotes: z.array(z.string()).min(1).max(20) }),
execute: async ({ url, quotes }) => {
try {
const res = await payingFetch(`${ORIGIN}/v1/verify/quotes`, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ url, quotes }),
});
return await res.json();
} catch (e) {
return { error: e.message }; // payment refused by the policy above, network error: tell the agent why
}
},
});
const question = process.argv.slice(2).join(' ')
|| 'Does https://www.iana.org/help/example-domains say "These domains may be used as illustrative examples in documents" and "without prior coordination with us"?';
const { text } = await generateText({
model: anthropic(process.env.MODEL || 'claude-sonnet-5-5'),
tools: { verify_quotes: verifyQuotes },
stopWhen: isStepCount(5),
instructions: 'Before you quote a web page, call verify_quotes once with every quote you plan to use from it. '
+ 'Cite a quote only when its match is exact or fuzzy and page.content_kind is real. '
+ 'If the tool returns an error, say what it was and stop.',
prompt: question,
});
console.log(text);
node agent-http.mjs # pays US$0.02 per call in USDC
The first request gets a 402 with the price in the PAYMENT-REQUIRED header; @x402/fetch signs the payment and sends it again. The policy keeps only offers on Base (eip155:8453) of at most US$0.02; anything else is refused before anything is signed, and the tool returns the reason as error. A request the service refuses (bad URL, empty quote) is refused before payment and not charged.
What comes back
results[i].match:exact,fuzzyornone, in the order ofquotes. Readpage.content_kindfirst:noneon abot_wallorjs_shellpage means the page could not be read.receipt: signed evidence of what the page said and when our fetcher saw it, as a JWS.POST /v1/receipt/verifychecks it for free, or check it offline with [verify-receipt.mjs](https://vehcdj664efetfrsolne5umanq.srv.us/docs/verify-offline). A receipt records what was on the page at that time, not whether a statement on it is correct.- Try the answer shape first with the free sample:
GET https://vehcdj664efetfrsolne5umanq.srv.us/v1/sample/quotes.
Terms
Pay per call, no account. See Payments, Limits and the Terms.