Does this page answer my question?
Send a URL and a question. Get back whether the page holds what is needed to answer it, how closely the page is about the subject, and the one paragraph that answers it — quoted from the page by code, never written by a model, so there is nothing invented to check. For an agent deciding which of ten search results is worth reading.
POST https://api.bottrunk.com/s/page-fit
Call it
# once, on the machine your agent runs on npx bottrunk-mcp wallet # prints the address; send it 0.3 ALGO, then USDC claude mcp add bottrunk -- npx -y bottrunk-mcp # then ask your agent for it — the tool is `bottrunk_page_fit`
# Ask. You get a 402 with the payment requirements.
curl https://api.bottrunk.com/s/page-fit \
-H "Content-Type: application/json" \
-d '{"url":"https://en.wikipedia.org/wiki/Honduras","question":"What is the capital of Honduras?"}'
# Pay and retry. Any x402 client does this for you.
curl https://api.bottrunk.com/s/page-fit \
-H "PAYMENT-SIGNATURE: <signed USDC transfer>" \
-H "Content-Type: application/json" \
-d '{"url":"https://en.wikipedia.org/wiki/Honduras","question":"What is the capital of Honduras?"}'
# pip install "x402-avm[avm,requests]==2.0.2"
import base64, os
from algosdk import encoding, mnemonic
from x402 import x402ClientSync
from x402.http import x402HTTPClientSync
from x402.http.clients.requests import x402_requests
from x402.mechanisms.avm import ALGORAND_MAINNET_CAIP2
from x402.mechanisms.avm.exact import ExactAvmScheme
class MnemonicSigner: # signs the payment with your agent's 25-word mnemonic
def __init__(self, words):
self._sk = mnemonic.to_private_key(words)
self.address = encoding.encode_address(base64.b64decode(self._sk)[32:])
def sign_transactions(self, unsigned_txns, indexes_to_sign):
return [base64.b64decode(encoding.msgpack_encode(
encoding.msgpack_decode(base64.b64encode(t).decode()).sign(self._sk)))
if i in indexes_to_sign else None
for i, t in enumerate(unsigned_txns)]
# A wallet holding 0.3 ALGO, opted in to USDC, funded with the USDC it may spend.
signer = MnemonicSigner(os.environ["ALGORAND_MNEMONIC"])
client = x402ClientSync().register(ALGORAND_MAINNET_CAIP2,
ExactAvmScheme(signer, algod_url="https://mainnet-api.algonode.cloud"))
with x402_requests(client) as session: # answers the 402, pays, retries
r = session.post("https://api.bottrunk.com/s/page-fit", json={"url": "https://en.wikipedia.org/wiki/Honduras", "question": "What is the capital of Honduras?"})
print(r.json())
receipt = x402HTTPClientSync(client).get_payment_settle_response(r.headers.get)
print(receipt.transaction) # the settlement on-chain
// npm install @x402-avm/fetch @x402-avm/avm @x402-avm/core algosdk
import algosdk from "algosdk";
import { x402Client } from "@x402-avm/core/client";
import { ExactAvmScheme } from "@x402-avm/avm/exact/client";
import { toClientAvmSigner } from "@x402-avm/avm";
import { wrapFetchWithPayment } from "@x402-avm/fetch";
// A wallet holding 0.3 ALGO, opted in to USDC, funded with the USDC it may spend.
const { sk } = algosdk.mnemonicToSecretKey(process.env.ALGORAND_MNEMONIC!);
const signer = toClientAvmSigner(Buffer.from(sk).toString("base64"));
const client = new x402Client().register("algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=",
new ExactAvmScheme(signer, { algodUrl: "https://mainnet-api.algonode.cloud" })); // default is TestNet
const fetchWithPay = wrapFetchWithPayment(fetch, client);
const r = await fetchWithPay("https://api.bottrunk.com/s/page-fit", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({"url":"https://en.wikipedia.org/wiki/Honduras","question":"What is the capital of Honduras?"}),
});
console.log(await r.json());
How it behaves
The decisions this service makes on your behalf, and the ones that end in a refusal. A 4xx is never settled, so a refusal costs you nothing.
| Evidence | The paragraph's text exactly as the page has it, with markdown syntax removed (images, link targets, table pipes, footnote marks). Returned only when answers is 0.5 or more; otherwise evidence is null. |
| Paragraphs | Blocks under 20 characters are skipped, blocks over 1,500 are cut at line breaks, and at most 254 are considered. A page with none is refused with 422 and not charged. |
| What is read | The page as scrape-markdown sees it, cut to its first 30,000 characters; truncated says when. Its refusals come back unchanged and are not charged. |
| Question | Up to 1,000 characters. Empty or longer is refused with 422. |
| Who judges | Your input is sent to a third-party model provider to be judged, with questions BotTrunk wrote; you cannot change the questions. |
| Advisory | Probabilities, not proof. Do not make this the only check before something that cannot be undone. The raw probabilities come back so you can set your own thresholds. |
| Numbers | Probabilities are rounded to 0.01. Severity and relevance are on a 0–2 scale and can be fractional. version changes whenever a question or threshold changes. |
| If the judge is unavailable | The call fails with 502 and is never settled, so it costs you nothing; neither does a 422. |
| Size and time | Pages over 2 MB are refused with page too large. Connect times out at 5 s, the whole fetch at 20 s, and at most 3 redirects are followed. |
| Who we look like | Requests go out as BotTrunk/0.1 (+https://bottrunk.com/docs). Sites that block unknown agents will block this one. |
| Private addresses | Hostnames that resolve to private, loopback or link-local space are refused with 422 before any request is made. Do not point this at localhost or an intranet. |
| Method | POST only. A GET on the same path returns this page. |
Input
| url | string | Public http(s) URL. |
| question | string | Required. What the agent wants to know, up to 1,000 characters. |
Output
| answers | number | P(the page holds what is needed to answer). |
| relevance | number | 0 unrelated, 1 related topic, 2 the subject itself. |
| evidence | string | The answering paragraph, quoted, or null. |
| evidence_confidence | number | How clearly that paragraph won, 0–1. |
| title | string | Document title. |
| truncated | boolean | Whether the page was cut at 30,000 characters. |
| version | string | Rubric version. |