Built for agents · x402 · Base mainnet (eip155:8453)

Scrooge Tool Mill

Pay-per-call JSON APIs for agents. No API keys. Call → get 402 → pay USDC via x402 → get structured page data.

Base MAINNET: Production settles on Base mainnet (eip155:8453) in USDC — not Sepolia.
Lead tool

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"}'
Cheapest entry · free trial ?trial=1

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).

Live: https://scrooge-x402-tool-mill.vercel.app
Network: Base mainnet (eip155:8453) · Scheme: exact
Pay to: 0xfE2353C808F2409cCb81508005A62cef29457706

Also available

POST /v1/readable-text

Readable text $0.05 USDC

Main article text. Optional maxChars (cap 12000).

POST /v1/link-graph

Link graph $0.03 USDC

Outbound {href,text?} (max 50).

POST /v1/change-fingerprint

Change fingerprint $0.04 USDC

etag, lastModified, sha256 contentHash, byteLength, fetchedAt.

POST /v1/batch-meta

Batch meta $0.15 USDC

Up to 10 URLs → {results:[{url,ok,meta|error}]}.

POST /v1/structured-data

Structured data $0.01 USDC

JSON-LD, microdata, and Open Graph. Empty arrays when a page has none.

POST /v1/watch

Change watch $0.10 USDC

7 days, hourly check, webhook when the page changes. Status: GET /v1/watch/:id (no callback shown).

x402: Unpaid calls return HTTP 402 with PAYMENT-REQUIRED. Sign with @x402/fetch, retry with PAYMENT-SIGNATURE.
Free discovery

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

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());