Base URL: https://api.webextract.dev (same app at https://webextract.dev). Machine-readable: /openapi.json · /llms.txt · /.well-known/x402.
Framework guides: Vercel AI SDK (@webextract/ai-sdk) · LangChain (langchain-webextract for Python, @webextract/langchain for JS).
| Endpoint | Returns | Prepaid | x402 |
|---|---|---|---|
GET /v1/extract?url=<page> | Markdown + metadata, product, tables | 1 credit | $0.005 |
GET /v1/markdown?url=<page> | Markdown, title, word count | 0.4 credit | $0.002 |
GET /v1/balance | Credits left (or free calls left today for your IP) | free | — |
product.price_detail is the recommended price field. Each entry in prices[] has type (list, sale, current), amount, currency, and the matched variant when the page proves it. It never contains 0. When no price can be trusted, status is unresolved and unresolved.reason says why (for example no_offer_in_structured_data, variant_not_matched, zero_price). When the URL selects a variant (?variant=, ?sku=, ?size=…), the flat product.price is that variant's price. If the variant cannot be matched on a page whose variants have different prices, product.price is null instead of another variant's price. Listing and category pages return product: null with product_unresolved.reason: "listing_page". content_source is raw_html (no JavaScript is executed); product.source is the extraction method (json-ld, opengraph, microdata, heuristic).
Both data endpoints also accept POST with a JSON body {"url": "https://..."} (or a form body).
curl -H "X-Billing: free" "https://api.webextract.dev/v1/extract?url=https://example.com"
curl -X POST https://api.webextract.dev/v1/markdown -H "X-Billing: free" -H "Content-Type: application/json" -d '{"url":"https://example.com"}'
X-Billing: free (or ?free=1). X-Free-Remaining is returned only in free mode. Without the opt-in, a request with no key and no payment gets HTTP 402 x402_required. MCP keeps 50 free calls/day with no header (same per-IP counter). Cloud and agent egress IPs can rotate, so the counter may vary.Authorization: Bearer <key>. Buy credits at /#pricing; credits never expire. X-Credits-Charged / X-Credits-Remaining headers.exact, USDC on Base (recommended), Polygon, Arbitrum One, World Chain, Monad, Avalanche C-Chain or OP Mainnet. Ethereum mainnet is not supported. Payment settles at the quoted price and only when the request succeeds.| Network | CAIP-2 | USDC |
|---|---|---|
| Base | eip155:8453 | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 |
| Polygon | eip155:137 | 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359 |
| Arbitrum One | eip155:42161 | 0xaf88d065e77c8cC2239327C5EDb3A432268e5831 |
| World Chain | eip155:480 | 0x79A02482A880bCE3F13e09Da970dC34db4CD24d1 |
| Monad | eip155:143 | 0x754704Bc059F8C67012fEd69BC8A327a5aafb603 |
| Avalanche C-Chain | eip155:43114 | 0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E |
| OP Mainnet | eip155:10 | 0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85 |
X-Billing header: free opts into the free tier. x402 returns the 402 with payment requirements (reason x402_requested). x402-testnet uses the sandbox (below). Any other value returns 400 invalid_billing_mode.
Order of checks: 1) url validation (400, never billed) → 2) Authorization: Bearer key (credits; wins over everything below) → 3) payment header PAYMENT-SIGNATURE (wins over X-Billing: free) → 4) X-Billing: x402 → 402 x402_requested → 5) X-Billing: free or ?free=1 → free quota, or 402 free_limit_reached with X-Free-Remaining: 0 → 6) otherwise 402 x402_required.
Note on ?free=1: it is read from the API's own query string. If your target URL has its own query string, URL-encode it (curl -G --data-urlencode "url=https://a.com/?x=1" -d free=1 ...) or simply use the header.
SDK note: official x402 SDKs (@x402/evm 2.28, Python x402 2.25) do not include OP Mainnet and World Chain USDC in their default asset lists; add them to spendControls.allowedAssets, and register only the network(s) where your wallet holds USDC (or pass a requirements selector), otherwise the SDK picks the first option (Base).
The x402 v2 requirements are in the PAYMENT-REQUIRED response header (base64 JSON). Sign one option and retry with PAYMENT-SIGNATURE. The JSON body says why payment is needed; the same value is in the X-Payment-Reason header:
reason | Meaning |
|---|---|
x402_required | No key, no payment and no free opt-in. Pay with x402, send a Bearer key, or retry with X-Billing: free while your IP has free calls left. |
x402_requested | You sent X-Billing: x402 (or used the sandbox) and no payment yet. |
free_limit_reached | You opted into the free tier but your IP's free daily calls are used up (X-Free-Remaining: 0). Retrying with X-Billing: free will not help today. |
invalid_payment | A payment was sent but rejected; detail has a short reason code (e.g. insufficient funds, expired, bad signature) or a decode error. Nothing was charged. |
unsupported_network | The payment used a network not offered for this endpoint. |
settlement_failed | The payment was verified but on-chain settlement did not complete. Nothing was charged (charged: false, retryable: true). Retry the request with a freshly signed payment. |
{"error":"payment_required","reason":"x402_required","message":"Payment required. ... or try it free: add header 'X-Billing: free' ...",
"x402":{"price":"$0.005","network":"eip155:8453","networks":["eip155:8453","eip155:137",...],"asset":"USDC"},
"stripe":{"buy_url":"https://webextract.dev/#pricing","packs":[...]}}
With a prepaid key and no credits left you get 402 {"error":"no_credits"}.
All errors are JSON and never charged: 400 missing or invalid url (checked before any billing) or unknown X-Billing value · 401 invalid API key · 402 payment required / out of credits · 413 page over 3 MB · 415 unsupported content type · 422 target site blocked or errored · 451 robots.txt disallows the URL.
Test an x402 client end to end without real money. Same endpoints and responses:
https://api.webextract.dev/sandbox/v1/extract?url=... https://api.webextract.dev/sandbox/v1/markdown?url=... # or on the normal endpoints: curl -H "X-Billing: x402-testnet" "https://api.webextract.dev/v1/markdown?url=https://example.com"
eip155:84532) test USDC 0x036CbD53842c5426634e7929541eC2318f3dCF7e (EIP-712 name USDC, version 2), settled by the https://x402.org/facilitator testnet facilitator. Get test USDC at faucet.circle.com.Retry-After).Remote streamable-http server: https://api.webextract.dev/mcp (stateless JSON). Tools extract_url and page_markdown, both with outputSchema and structuredContent. Registry name dev.webextract/extract. Server card: /.well-known/mcp/server-card.json. MCP calls use the free tier (50/day per IP, no header needed) or a Bearer key; for pay-per-call use the REST endpoints with x402.
We identify as WebExtractBot, obey robots.txt, never bypass CAPTCHAs or anti-bot protection, and refuse private network addresses.