Scrape a URL
POST /v1/scrape fetches a URL with a stealth browser and returns its content in one
of five output formats. Markdown is the default and is optimized for LLM consumption.
Authentication: API key or x402 payment. On x402 requests, payment is verified
before the scrape and settled only on success.
Endpoint
POST https://api.tazpal.com/v1/scrape
Request
Headers
| Header | Required | Description |
|---|---|---|
Authorization: Bearer baas_live_... | one of | API key authentication |
X-PAYMENT: <base64 permit> | one of | x402 payment |
Content-Type: application/json | yes | Must be application/json |
X-Request-ID | no | Client-supplied request ID (echoed in X-Request-ID response header) |
Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
url | string | ✅ | — | Target URL to scrape |
output | string | no | markdown | One of markdown, screenshot, pdf, csv, html |
options | object | no | {} | Output-specific options (see below) |
wait_for_selector | string | no | — | CSS selector to wait for before extraction |
timeout_ms | integer | no | 20000 | Navigation timeout in ms (1000–120000) |
block_media | boolean | no | true | Block image/font/video requests for speed |
proxy_url | string | no | — | Explicit proxy (socks5:// or http://) |
wait_strategy | string | no | auto | default, spa, heavy, or cloudflare |
retry | boolean | no | true | Retry on failure with exponential backoff |
bypass_cache | boolean | no | false | Skip the 5-minute response cache |
javascript | string | no | — | Custom JS to execute after page load |
Options by output format
screenshot options
screenshot options
| Key | Type | Default | Description |
|---|---|---|---|
format | string | png | png or jpeg |
full_page | boolean | true | Capture the full page (PNG only) |
width / height | integer | — | Explicit viewport size |
quality | integer | 90 | JPEG quality |
pdf options
pdf options
| Key | Type | Description |
|---|---|---|
format | string | Page size (e.g. A4, Letter) |
landscape | boolean | Landscape orientation |
print_background | boolean | Print background graphics |
scale | number | Render scale (e.g. 0.8) |
margin | object | {top, bottom, left, right} margins |
quality | integer | JPEG quality for the screenshot fallback |
csv options
csv options
| Key | Type | Default | Description |
|---|---|---|---|
table_selector | string | table | CSS selector for tables to extract |
Response
Success — 200 OK
| Field | Type | Description |
|---|---|---|
status | integer | Always 200 |
url | string | The scraped URL |
output | string | The output format used |
data | object | ScrapeData for markdown; format-specific dict otherwise |
execution_time_ms | integer | End-to-end time including browser launch |
payment | object | null | x402 settlement result (null for API-key auth) |
data shape:
| Field | Type | Description |
|---|---|---|
title | string | Page title |
markdown | string | Clean Markdown content |
character_count | integer | Length of the Markdown |
Examples
Markdown (default)
curl -X POST https://api.tazpal.com/v1/scrape \
-H "Authorization: Bearer baas_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://news.ycombinator.com"}'
import httpx
resp = httpx.post(
"https://api.tazpal.com/v1/scrape",
headers={"Authorization": "Bearer baas_live_YOUR_KEY"},
json={"url": "https://news.ycombinator.com"},
)
resp.raise_for_status()
data = resp.json()["data"]
print(data["title"])
print(data["markdown"][:500])
const resp = await fetch("https://api.tazpal.com/v1/scrape", {
method: "POST",
headers: {
Authorization: "Bearer baas_live_YOUR_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ url: "https://news.ycombinator.com" }),
});
const { data } = await resp.json();
console.log(data.title, data.markdown.slice(0, 500));
Screenshot (base64 PNG)
- API key
- x402
curl -X POST https://api.tazpal.com/v1/scrape \
-H "Authorization: Bearer baas_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com", "output": "screenshot", "options": {"full_page": true}}'
curl -X POST https://api.tazpal.com/v1/scrape \
-H "X-PAYMENT: eyJwYXlsb2FkIjp7Im5vbmNlIjoi..." \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com", "output": "screenshot"}'
Response
{
"status": 200,
"url": "https://example.com",
"output": "screenshot",
"data": {
"data": "iVBORw0KGgoAAAANSUhEUgAA...",
"format": "png",
"size_bytes": 48102
},
"execution_time_ms": 2210,
"payment": null
}
curl -X POST https://api.tazpal.com/v1/scrape \
-H "Authorization: Bearer baas_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com", "output": "pdf", "options": {"format": "A4", "print_background": true}}'
CSV (table extraction)
curl -X POST https://api.tazpal.com/v1/scrape \
-H "Authorization: Bearer baas_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/data", "output": "csv", "options": {"table_selector": "table.prices"}}'
Custom JavaScript + wait strategy
import httpx
resp = httpx.post(
"https://api.tazpal.com/v1/scrape",
headers={"Authorization": "Bearer baas_live_YOUR_KEY"},
json={
"url": "https://example.com",
"wait_strategy": "spa",
"javascript": "document.querySelector('#load-more').click()",
},
)
Errors
| Status | detail.error | Meaning |
|---|---|---|
400 | invalid_output | output is not a valid format |
400 | connection_failed | The URL could not be reached (DNS/connection) |
401 | unauthorized | Missing or invalid API key / X-PAYMENT |
429 | rate_limited | Per-IP rate limit exceeded |
500 | scrape_failed | Unexpected engine failure |
504 | timeout | Navigation timed out |
Error example
{
"detail": {
"error": "timeout",
"message": "Navigation timed out after 20000ms: https://slow.example.com",
"url": "https://slow.example.com",
"payment_charged": false
}
}
Error responses carry a
payment_charged boolean so x402 callers can confirm they
were not charged for a failed scrape.