switch402

switch402 API docs and migration snippets

One pay-per-call API that returns any public page as markdown, plus site map, page metadata, screenshot, web search and PDF to text tools. Paid per successful call over x402 in USDC on Base: no signup, API key or subscription required (optional prepaid credits below). If a call fails you are not charged.

Endpoints

  • POST https://api.switch402.com/v1/scrape

    Body {"url": "<page>"}. Page as markdown. $0.008 USDC on Base per successful call (x402).

  • GET https://api.switch402.com/v1/scrape?url=<page>

    Same as POST, URL in the query string.

  • POST https://api.switch402.com/v1/scrape/lite

    Body {"url": "<page>"}. Scrape lite: page as markdown, $0.003 per successful call.

  • POST https://api.switch402.com/v1/map

    Body {"url": "<page>", "limit": 200}. Site map: the same-site links on the page plus the URLs in its sitemap (from robots.txt or /sitemap.xml), deduped, up to limit (max 1,000). $0.001 per successful call. GET ?url=<page>&limit=200 also works.

  • POST https://api.switch402.com/v1/metadata

    Body {"url": "<page>"}. Page metadata: title, meta description, canonical URL, language, OpenGraph and Twitter tags, and JSON-LD blocks. $0.001 per successful call. GET ?url=<page> also works.

  • POST https://api.switch402.com/v1/screenshot

    Body {"url": "<page>"}. Screenshot of the visible 1280x800 viewport, JavaScript rendered, as a base64 PNG. $0.008 per successful call. GET ?url=<page> also works.

  • POST https://api.switch402.com/v1/screenshot/full

    Body {"url": "<page>"}. Full-page screenshot: the whole scrollable page as a base64 PNG. $0.012 per successful call. GET ?url=<page> also works.

  • POST https://api.switch402.com/v1/search

    Body {"q": "<query>", "limit": 10}. Web search: up to 10 ranked results with title, URL and snippet. $0.008 per successful call. GET ?q=<query>&limit=10 also works.

  • POST https://api.switch402.com/v1/pdf

    Body {"url": "<pdf>", "pages": [1, 2]}. PDF to text: the text of a public PDF, page by page, up to 10 MB and 50 pages. pages is optional (a list or a range like "1-3,7"; default the first 50). $0.008 per successful call. GET ?url=<pdf>&pages=1-3 also works.

  • GET https://api.switch402.com/v1/quote?url=<page>&tier=<standard|lite>

    Free. Price and payment details before you call.

  • GET https://api.switch402.com/health

    Free. Service status.

  • POST https://api.switch402.com/mcp

    MCP (JSON-RPC 2.0) transport for agents.

  • GET https://api.switch402.com/openapi.json

    Full OpenAPI spec.

Pricing

  • /v1/scrape $0.008 per page: any public page as clean markdown, with failover
  • /v1/scrape/lite $0.003 per page: a page as markdown, charged only on success
  • /v1/map $0.001 per call: a site's pages from its links and sitemap
  • /v1/metadata $0.001 per call: a page's title, description, social tags and JSON-LD
  • /v1/screenshot $0.008 per call: the visible 1280x800 viewport as a PNG
  • /v1/screenshot/full $0.012 per call: the whole scrollable page as a PNG
  • /v1/search $0.008 per call: up to 10 ranked web results with title, URL and snippet
  • /v1/pdf $0.008 per call: the text of a public PDF, page by page (up to 10 MB, 50 pages)
  • /v1/quote free: price and availability before you pay

USDC on Base over x402, charged only on success. Optional prepaid credits and a $10 monthly lite plan are described below. Map and metadata fetch public http(s) pages only, follow at most 5 redirects and read up to 2 MB per page. Screenshots take public http(s) URLs only and return a PNG up to 8 MB. Search takes a 1-400 character query and returns up to 10 results. PDF to text takes a public http(s) PDF URL that declares its size, up to 10 MB, and returns up to 50 pages of text; scanned (image-only) or password-protected PDFs return an error and are not charged.

Response (200)

{
  "url": "https://example.com/",
  "title": "Example Domain",
  "markdown": "# Example Domain\n\nThis domain is for use in documentation examples...",
  "own_service": false,
  "ms": 1800,
  "attempts": 1
}

Prepaid credits and the monthly plan

Prefer a balance to paying each call? Buy a $5, $20 (+5% credit) or $50 (+10% credit) pack once with x402 and get an API key. Send it as Authorization: Bearer <key>. Each call debits that endpoint's own price, only when it succeeds: /v1/scrape/lite $0.003 per page, /v1/scrape $0.008 per page, /v1/map and /v1/metadata $0.001 per call, /v1/screenshot $0.008, /v1/screenshot/full $0.012, /v1/search $0.008 and /v1/pdf $0.008 per call. Failed calls cost nothing.

Monthly plan, $10 for 30 days: during the period, lite pages bill at the plan rate, so $10 covers 4,000 lite pages. You pay for pages you actually use. Nothing expires: leftover balance stays on your key and is debited at the normal endpoint prices after the period ends. Lost a key? The wallet that bought it can move the balance to a new key with a signed, single-use challenge.

  • $5 pack: $5.00 of balance
  • $20 pack: $21.00 of balance (+5% credit)
  • $50 pack: $55.00 of balance (+10% credit), one per wallet per day
  • Monthly plan, $10 for 30 days: $10.00 of balance; lite pages bill at $0.0025 during the period, so $10 covers 4,000 lite pages
  • POST https://api.switch402.com/v1/credits/buy/5

    Buy the $5 pack over x402. Also /buy/20, /buy/50 and /buy/monthly. Returns your key once.

  • GET https://api.switch402.com/v1/credits

    Free with your key. Balance and plan status.

  • POST https://api.switch402.com/v1/credits/rotate

    Swap your key for a new one; the balance moves with it.

  • POST https://api.switch402.com/v1/credits/recover/nonce

    Lost key: get a single-use challenge, sign it with the buying wallet, then POST /v1/credits/recover.

Limits per key: 60 calls per minute, 3,000 calls and $5 of spend per day. Up to 3 purchases per wallet per day. Pay-per-call with x402 keeps working without a key.

// Buy once with any x402 client (see the JavaScript client below)
const buy = await fetchWithPayment('https://api.switch402.com/v1/credits/buy/5', { method: 'POST' })
const { key, key_id } = await buy.json() // the key is shown once: store it now

// Then call with the key: no per-call payment step
const res = await fetch('https://api.switch402.com/v1/scrape/lite', {
  method: 'POST',
  headers: { Authorization: 'Bearer ' + key, 'Content-Type': 'application/json' },
  body: JSON.stringify({ url: 'https://example.com' }),
})

Migrate from Firecrawl

Firecrawl scrape returns the page in data.markdown. switch402 returns it in markdown at the top level. No API key: the x402 client pays per call.

curl: before (Firecrawl)

curl -X POST https://api.firecrawl.dev/v2/scrape \
  -H 'Authorization: Bearer fc-YOUR_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://example.com","formats":["markdown"]}'

curl: after (switch402)

# Unpaid call returns HTTP 402 with the x402 payment requirements;
# an x402 client (JS / Python below) pays $0.008 USDC on Base and retries.
curl -i -X POST https://api.switch402.com/v1/scrape \
  -H 'Content-Type: application/json' \
  -H 'User-Agent: my-agent/1.0' \
  -d '{"url":"https://example.com"}'

JavaScript

// npm i @x402/fetch @x402/evm viem
import { wrapFetchWithPaymentFromConfig } from '@x402/fetch'
import { ExactEvmScheme } from '@x402/evm'
import { privateKeyToAccount } from 'viem/accounts'

const account = privateKeyToAccount(process.env.AGENT_KEY)
const fetchWithPayment = wrapFetchWithPaymentFromConfig(fetch, {
  schemes: [{ network: 'eip155:8453', client: new ExactEvmScheme(account) }],
})

// before: const { data } = await firecrawlScrape(url)  ->  data.markdown
const res = await fetchWithPayment('https://api.switch402.com/v1/scrape', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'User-Agent': 'my-agent/1.0' },
  body: JSON.stringify({ url: 'https://example.com' }),
})
const { markdown, title } = await res.json()

Python

# pip install "x402[requests,evm]"
import os
from eth_account import Account
from x402 import x402ClientSync
from x402.http.clients import x402_requests
from x402.mechanisms.evm import EthAccountSigner
from x402.mechanisms.evm.exact import ExactEvmScheme

client = x402ClientSync()
client.register("eip155:8453", ExactEvmScheme(signer=EthAccountSigner(Account.from_key(os.environ["AGENT_KEY"]))))
session = x402_requests(client)
session.headers["User-Agent"] = "my-agent/1.0"

# before: requests.post("https://api.firecrawl.dev/v2/scrape", ...).json()["data"]["markdown"]
r = session.post("https://api.switch402.com/v1/scrape", json={"url": "https://example.com"})
r.raise_for_status()
markdown = r.json()["markdown"]

Migrate from ScrapingBee

ScrapingBee takes the url as a query parameter with your key in an Authorization: Bearer header, and returns the page HTML (JavaScript rendering is on by default; return_page_markdown=true returns markdown). switch402 takes the same url (GET ?url= or a JSON body) and returns markdown in JSON, with no key: the x402 client pays per call.

curl: before (ScrapingBee)

curl -G https://app.scrapingbee.com/api/v1 \
  -H 'Authorization: Bearer YOUR-API-KEY' \
  --data-urlencode 'url=https://example.com' \
  --data-urlencode 'return_page_markdown=true'

curl: after (switch402)

# Same query-string style; unpaid call returns HTTP 402 with x402 payment requirements.
curl -i -G https://api.switch402.com/v1/scrape \
  -H 'User-Agent: my-agent/1.0' \
  --data-urlencode 'url=https://example.com'

JavaScript

// npm i @x402/fetch @x402/evm viem
import { wrapFetchWithPaymentFromConfig } from '@x402/fetch'
import { ExactEvmScheme } from '@x402/evm'
import { privateKeyToAccount } from 'viem/accounts'

const account = privateKeyToAccount(process.env.AGENT_KEY)
const fetchWithPayment = wrapFetchWithPaymentFromConfig(fetch, {
  schemes: [{ network: 'eip155:8453', client: new ExactEvmScheme(account) }],
})

// before: fetch('https://app.scrapingbee.com/api/v1?url=' + encodeURIComponent(url), { headers: { Authorization: 'Bearer YOUR-API-KEY' } })
const url = 'https://example.com'
const res = await fetchWithPayment('https://api.switch402.com/v1/scrape?url=' + encodeURIComponent(url), {
  headers: { 'User-Agent': 'my-agent/1.0' },
})
const { markdown } = await res.json()

Python

# pip install "x402[requests,evm]"
import os
from eth_account import Account
from x402 import x402ClientSync
from x402.http.clients import x402_requests
from x402.mechanisms.evm import EthAccountSigner
from x402.mechanisms.evm.exact import ExactEvmScheme

client = x402ClientSync()
client.register("eip155:8453", ExactEvmScheme(signer=EthAccountSigner(Account.from_key(os.environ["AGENT_KEY"]))))
session = x402_requests(client)
session.headers["User-Agent"] = "my-agent/1.0"

# before: requests.get("https://app.scrapingbee.com/api/v1", params={"url": url}, headers={"Authorization": "Bearer YOUR-API-KEY"}).text
r = session.get("https://api.switch402.com/v1/scrape", params={"url": "https://example.com"})
r.raise_for_status()
markdown = r.json()["markdown"]

Full spec: openapi.json · llms.txt for agents · quickstart

More pay-per-call calculators