# Pydantic AI: check several quotes on one page

Last updated: 2026-10-13

A Pydantic AI agent in Python 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 the `x402` Python package.

The same check for other frameworks: [LangChain](https://vehcdj664efetfrsolne5umanq.srv.us/examples/langchain.md), [LlamaIndex](https://vehcdj664efetfrsolne5umanq.srv.us/examples/llamaindex.md), [OpenAI Agents SDK](https://vehcdj664efetfrsolne5umanq.srv.us/examples/openai-agents.md), [Claude Agent SDK](https://vehcdj664efetfrsolne5umanq.srv.us/examples/claude-agent-sdk.md), [Vercel AI SDK](https://vehcdj664efetfrsolne5umanq.srv.us/examples/vercel-ai-sdk.md), [CrewAI](https://vehcdj664efetfrsolne5umanq.srv.us/examples/crewai.md) and [Mastra](https://vehcdj664efetfrsolne5umanq.srv.us/examples/mastra.md).

## Install

Python 3.10 or later.

```sh
pip install "pydantic-ai-slim[anthropic]" "x402[requests,evm]"
export ANTHROPIC_API_KEY=...
export ATTESTPAGE_X402_KEY=0x...
```

Any model Pydantic AI supports works; set `MODEL` (for example `openai:gpt-5`) and install its extra. `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.py

```python
"""Pydantic AI agent that checks quotes with POST /v1/verify/quotes, paying per call with x402."""
import os
import sys

from eth_account import Account
from pydantic_ai import Agent
from x402 import x402ClientSync
from x402.http.clients import x402_requests
from x402.mechanisms.evm.exact import ExactEvmScheme
from x402.mechanisms.evm.signers import EthAccountSigner

ORIGIN = os.environ.get("ATTESTPAGE_URL", "https://vehcdj664efetfrsolne5umanq.srv.us").rstrip("/")
MAX_UNITS = 20000  # US$0.02 in USDC (6 decimals): the most one call may cost

client = x402ClientSync()
client.register("eip155:8453", ExactEvmScheme(signer=EthAccountSigner(Account.from_key(os.environ["ATTESTPAGE_X402_KEY"]))))
client.register_policy(lambda _version, reqs: [
    r for r in reqs if r.network == "eip155:8453" and int(r.get_amount()) <= MAX_UNITS
])
session = x402_requests(client)

agent = Agent(
    os.environ.get("MODEL", "anthropic:claude-sonnet-5-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."
    ),
)


@agent.tool_plain
def verify_quotes(url: str, quotes: list[str]) -> dict:
    """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."""
    try:
        res = session.post(f"{ORIGIN}/v1/verify/quotes", json={"url": url, "quotes": quotes}, timeout=60)
    except Exception as e:  # the policy above refused every offer, or the network failed
        return {"error": f"{e}. This recipe pays at most US$0.02 in USDC on eip155:8453."}
    if res.status_code == 402:  # not paid: no offer was acceptable
        return {"error": "payment required", "payment_required": res.headers.get("PAYMENT-REQUIRED")}
    return res.json()


if __name__ == "__main__":
    question = " ".join(sys.argv[1:]) or (
        '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"?'
    )
    print(agent.run_sync(question).output)
```

```sh
python agent_http.py   # pays US$0.02 per call in USDC
```

The first request gets a 402 with the price in the `PAYMENT-REQUIRED` header; the `x402` session signs the payment and sends it again. The policy keeps only offers on Base (`eip155:8453`) of at most US$0.02, so a higher price or another network is refused before anything is signed. The tool returns that refusal, or a 402 that was not paid, to the agent as an `error` instead of raising, so the agent can report it and stop. A request the service refuses (bad URL, empty quote) is refused before payment and not charged.

## What comes back

- `results[i].match`: `exact`, `fuzzy` or `none`, in the order of `quotes`. Read `page.content_kind` first: `none` on a `bot_wall` or `js_shell` page 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/verify` checks 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](https://vehcdj664efetfrsolne5umanq.srv.us/docs/payments), [Limits](https://vehcdj664efetfrsolne5umanq.srv.us/docs/limits) and the [Terms](https://vehcdj664efetfrsolne5umanq.srv.us/terms).
