Scrooge Tool Mill
Pay-per-call JSON APIs for agents. No API keys. Call → get 402 → pay USDC via x402 → get structured page data.
eip155:8453) in USDC — not Sepolia.POST /v1/company-card · $0.03 USDC
Public-record company profile for a domain: legal name, domain age, registrar, LEI, socials. Each filled field names its source.
curl -sS -i -X POST https://scrooge-x402-tool-mill.vercel.app/v1/company-card \
-H 'content-type: application/json' \
-d '{"domain":"coinbase.com"}'
POST /v1/page-meta · $0.002 USDC
Title, description, canonical, OG, language, excerpt. Network eip155:8453 · pay to 0xfE2353C808F2409cCb81508005A62cef29457706. Free sample: GET /start. Agent start: GET /agent-start.json.
1) Probe (expect HTTP 402)
curl -sS -i -X POST https://scrooge-x402-tool-mill.vercel.app/v1/page-meta \
-H 'content-type: application/json' \
-d '{"url":"https://example.com"}'
2) Pay with @x402/fetch (Base mainnet USDC)
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.PRIVATE_KEY);
const fetchPaid = wrapFetchWithPaymentFromConfig(fetch, {
schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(account) }],
});
const res = await fetchPaid("https://scrooge-x402-tool-mill.vercel.app/v1/page-meta", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ url: "https://example.com" }),
});
// HTTP 200 → { url, title, description, ... }
Expected 200 shape: { url, title?, description?, canonical?, og?, language?, excerpt? } (fields present when the page exposes them).
Network: Base mainnet (eip155:8453) · Scheme: exact
Pay to:
0xfE2353C808F2409cCb81508005A62cef29457706
Also available
Readable text $0.05 USDC
Main article text. Optional maxChars (cap 12000).
Link graph $0.03 USDC
Outbound {href,text?} (max 50).
Change fingerprint $0.04 USDC
etag, lastModified, sha256 contentHash, byteLength, fetchedAt.
Batch meta $0.15 USDC
Up to 10 URLs → {results:[{url,ok,meta|error}]}.
Structured data $0.01 USDC
JSON-LD, microdata, and Open Graph. Empty arrays when a page has none.
Change watch $0.10 USDC
7 days, hourly check, webhook when the page changes. Status: GET /v1/watch/:id (no callback shown).
PAYMENT-REQUIRED. Sign with @x402/fetch, retry with PAYMENT-SIGNATURE.Machine-readable catalog
GET /.well-known/l402-services (canonical) · GET /.well-known/x402-services · GET /.well-known/x402.json · GET /openapi-x402.json. Thin GET on paid paths returns the same 402 challenge; POST is the workhorse.
MCP (streamable HTTP; add it in an MCP client, it is not a web page): https://scrooge-x402-tool-mill.vercel.app/mcp · server card
Pay on the first try
MCP setup
Claude Code: claude mcp add --transport http tool-mill https://scrooge-x402-tool-mill.vercel.app/mcpCursor (.cursor/mcp.json): {"mcpServers":{"tool-mill":{"url":"https://scrooge-x402-tool-mill.vercel.app/mcp"}}}Claude Desktop / stdio-only clients: {"mcpServers":{"tool-mill":{"command":"npx","args":["-y","mcp-remote","https://scrooge-x402-tool-mill.vercel.app/mcp"]}}}Any streamable-HTTP MCP client: https://scrooge-x402-tool-mill.vercel.app/mcp (POST, JSON-RPC)Paying: an unpaid tools/call returns x402 PaymentRequired (accepts[]) as an error result; an x402-aware client (@x402/mcp) signs and retries with _meta["x402/payment"].
Paid examples per route
POST /v1/company-card · $0.03 USDC · MCP company_card
# 1) unpaid call -> HTTP 402, requirements (x402Version 2, accepts[]) in body and PAYMENT-REQUIRED header
curl -i -X POST https://scrooge-x402-tool-mill.vercel.app/v1/company-card -H 'content-type: application/json' -d '{"domain":"coinbase.com"}'
# 2) retry with a signed payment (curl cannot sign; get it from @x402/fetch or an x402 wallet)
curl -X POST https://scrooge-x402-tool-mill.vercel.app/v1/company-card -H 'content-type: application/json' -H "PAYMENT-SIGNATURE: $SIGNED_PAYMENT" -d '{"domain":"coinbase.com"}'// npm i @x402/fetch @x402/evm viem (wallet needs $0.03 USDC on Base)
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const pay = wrapFetchWithPaymentFromConfig(fetch, { schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(account) }] });
const res = await pay("https://scrooge-x402-tool-mill.vercel.app/v1/company-card", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({"domain":"coinbase.com"}) });
console.log(res.status, await res.json());POST /v1/page-meta · $0.002 USDC · MCP page_meta
# 1) unpaid call -> HTTP 402, requirements (x402Version 2, accepts[]) in body and PAYMENT-REQUIRED header
curl -i -X POST https://scrooge-x402-tool-mill.vercel.app/v1/page-meta -H 'content-type: application/json' -d '{"url":"https://example.com"}'
# 2) retry with a signed payment (curl cannot sign; get it from @x402/fetch or an x402 wallet)
curl -X POST https://scrooge-x402-tool-mill.vercel.app/v1/page-meta -H 'content-type: application/json' -H "PAYMENT-SIGNATURE: $SIGNED_PAYMENT" -d '{"url":"https://example.com"}'// npm i @x402/fetch @x402/evm viem (wallet needs $0.002 USDC on Base)
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const pay = wrapFetchWithPaymentFromConfig(fetch, { schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(account) }] });
const res = await pay("https://scrooge-x402-tool-mill.vercel.app/v1/page-meta", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({"url":"https://example.com"}) });
console.log(res.status, await res.json());POST /v1/structured-data · $0.01 USDC · MCP structured_data
# 1) unpaid call -> HTTP 402, requirements (x402Version 2, accepts[]) in body and PAYMENT-REQUIRED header
curl -i -X POST https://scrooge-x402-tool-mill.vercel.app/v1/structured-data -H 'content-type: application/json' -d '{"url":"https://example.com"}'
# 2) retry with a signed payment (curl cannot sign; get it from @x402/fetch or an x402 wallet)
curl -X POST https://scrooge-x402-tool-mill.vercel.app/v1/structured-data -H 'content-type: application/json' -H "PAYMENT-SIGNATURE: $SIGNED_PAYMENT" -d '{"url":"https://example.com"}'// npm i @x402/fetch @x402/evm viem (wallet needs $0.01 USDC on Base)
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const pay = wrapFetchWithPaymentFromConfig(fetch, { schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(account) }] });
const res = await pay("https://scrooge-x402-tool-mill.vercel.app/v1/structured-data", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({"url":"https://example.com"}) });
console.log(res.status, await res.json());POST /v1/link-graph · $0.03 USDC · MCP link_graph
# 1) unpaid call -> HTTP 402, requirements (x402Version 2, accepts[]) in body and PAYMENT-REQUIRED header
curl -i -X POST https://scrooge-x402-tool-mill.vercel.app/v1/link-graph -H 'content-type: application/json' -d '{"url":"https://example.com","maxLinks":20}'
# 2) retry with a signed payment (curl cannot sign; get it from @x402/fetch or an x402 wallet)
curl -X POST https://scrooge-x402-tool-mill.vercel.app/v1/link-graph -H 'content-type: application/json' -H "PAYMENT-SIGNATURE: $SIGNED_PAYMENT" -d '{"url":"https://example.com","maxLinks":20}'// npm i @x402/fetch @x402/evm viem (wallet needs $0.03 USDC on Base)
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const pay = wrapFetchWithPaymentFromConfig(fetch, { schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(account) }] });
const res = await pay("https://scrooge-x402-tool-mill.vercel.app/v1/link-graph", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({"url":"https://example.com","maxLinks":20}) });
console.log(res.status, await res.json());POST /v1/change-fingerprint · $0.04 USDC · MCP change_fingerprint
# 1) unpaid call -> HTTP 402, requirements (x402Version 2, accepts[]) in body and PAYMENT-REQUIRED header
curl -i -X POST https://scrooge-x402-tool-mill.vercel.app/v1/change-fingerprint -H 'content-type: application/json' -d '{"url":"https://example.com"}'
# 2) retry with a signed payment (curl cannot sign; get it from @x402/fetch or an x402 wallet)
curl -X POST https://scrooge-x402-tool-mill.vercel.app/v1/change-fingerprint -H 'content-type: application/json' -H "PAYMENT-SIGNATURE: $SIGNED_PAYMENT" -d '{"url":"https://example.com"}'// npm i @x402/fetch @x402/evm viem (wallet needs $0.04 USDC on Base)
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const pay = wrapFetchWithPaymentFromConfig(fetch, { schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(account) }] });
const res = await pay("https://scrooge-x402-tool-mill.vercel.app/v1/change-fingerprint", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({"url":"https://example.com"}) });
console.log(res.status, await res.json());POST /v1/readable-text · $0.05 USDC · MCP readable_text
# 1) unpaid call -> HTTP 402, requirements (x402Version 2, accepts[]) in body and PAYMENT-REQUIRED header
curl -i -X POST https://scrooge-x402-tool-mill.vercel.app/v1/readable-text -H 'content-type: application/json' -d '{"url":"https://example.com","maxChars":4000}'
# 2) retry with a signed payment (curl cannot sign; get it from @x402/fetch or an x402 wallet)
curl -X POST https://scrooge-x402-tool-mill.vercel.app/v1/readable-text -H 'content-type: application/json' -H "PAYMENT-SIGNATURE: $SIGNED_PAYMENT" -d '{"url":"https://example.com","maxChars":4000}'// npm i @x402/fetch @x402/evm viem (wallet needs $0.05 USDC on Base)
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const pay = wrapFetchWithPaymentFromConfig(fetch, { schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(account) }] });
const res = await pay("https://scrooge-x402-tool-mill.vercel.app/v1/readable-text", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({"url":"https://example.com","maxChars":4000}) });
console.log(res.status, await res.json());POST /v1/watch · $0.10 USDC · MCP watch_page
# 1) unpaid call -> HTTP 402, requirements (x402Version 2, accepts[]) in body and PAYMENT-REQUIRED header
curl -i -X POST https://scrooge-x402-tool-mill.vercel.app/v1/watch -H 'content-type: application/json' -d '{"url":"https://example.com","callback_url":"https://your-app.example/hook"}'
# 2) retry with a signed payment (curl cannot sign; get it from @x402/fetch or an x402 wallet)
curl -X POST https://scrooge-x402-tool-mill.vercel.app/v1/watch -H 'content-type: application/json' -H "PAYMENT-SIGNATURE: $SIGNED_PAYMENT" -d '{"url":"https://example.com","callback_url":"https://your-app.example/hook"}'// npm i @x402/fetch @x402/evm viem (wallet needs $0.10 USDC on Base)
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const pay = wrapFetchWithPaymentFromConfig(fetch, { schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(account) }] });
const res = await pay("https://scrooge-x402-tool-mill.vercel.app/v1/watch", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({"url":"https://example.com","callback_url":"https://your-app.example/hook"}) });
console.log(res.status, await res.json());POST /v1/batch-meta · $0.15 USDC · MCP batch_meta
# 1) unpaid call -> HTTP 402, requirements (x402Version 2, accepts[]) in body and PAYMENT-REQUIRED header
curl -i -X POST https://scrooge-x402-tool-mill.vercel.app/v1/batch-meta -H 'content-type: application/json' -d '{"urls":["https://example.com","https://x402.org"]}'
# 2) retry with a signed payment (curl cannot sign; get it from @x402/fetch or an x402 wallet)
curl -X POST https://scrooge-x402-tool-mill.vercel.app/v1/batch-meta -H 'content-type: application/json' -H "PAYMENT-SIGNATURE: $SIGNED_PAYMENT" -d '{"urls":["https://example.com","https://x402.org"]}'// npm i @x402/fetch @x402/evm viem (wallet needs $0.15 USDC on Base)
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const pay = wrapFetchWithPaymentFromConfig(fetch, { schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(account) }] });
const res = await pay("https://scrooge-x402-tool-mill.vercel.app/v1/batch-meta", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({"urls":["https://example.com","https://x402.org"]}) });
console.log(res.status, await res.json());