Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
workflows, recipes, and evals layer for giving AI agents reliable access to
protected web data through Zenrows cloud infrastructure.**

It makes the Zenrows Universal Scraper API, Scraping Browser, and Residential
Proxies installable and usable directly from AI agents and developer workflows —
It makes the four Zenrows primitives — Fetch, Extract, Batch, and Browser
Sessions — installable and usable directly from AI agents and developer workflows —
so an agent can reliably access protected web data without hand-rolling anti-bot
handling, proxies, or browser rendering.

Expand All @@ -21,25 +21,25 @@ through AI agents, developers, and teams.
## 2. Why Zenrows

Normal fetch fails. Generic scrapers fail. Browser-first tools are expensive.
The Zenrows **Universal Scraper API** retrieves protected pages reliably and
structures them, while the **Scraping Browser** is there for the rare cases that
Zenrows **Fetch** retrieves protected pages reliably and **Extract** structures
them, while **Browser Sessions** are there for the rare cases that
need a real browser. Zenrows wins when the workflow runs over thousands or
millions of URLs.

## 3. Product architecture

The core product is the Zenrows **Universal Scraper API**
The core product is Zenrows **Fetch and Extract**
(`GET https://api.zenrows.com/v1/`). The CLI exposes it two ways: `zenrows fetch`
retrieves a protected page, and `zenrows extract` turns it into structured data
(JSON / CSS / Markdown). Both call the same endpoint — `extract` is just
that endpoint with extraction parameters, not a separate product.

| Command | What it does | Status (this build) |
| --- | --- | --- |
| `zenrows fetch` | Universal Scraper API — retrieve a protected page | **available** — `GET https://api.zenrows.com/v1/` |
| `zenrows extract` | Universal Scraper API — structured extraction (Autoparse / CSS / Markdown) | **available** — same `/v1/` |
| `zenrows batch` | Batch Scraper API — fan out over many URLs | beta — cloud works with beta access; local validate/estimate always |
| `zenrows browser` | Scraping Browser (CDP) / MCP escalation | experimental |
| `zenrows fetch` | Fetch — retrieve a protected page | **available** — `GET https://api.zenrows.com/v1/` |
| `zenrows extract` | Extract — structured extraction (Autoparse / CSS / Markdown) | **available** — same `/v1/` |
| `zenrows batch` | Batch — fan out over many URLs | beta — cloud works with beta access; local validate/estimate always |
| `zenrows browser` | Browser Sessions (CDP) / MCP escalation | experimental |
| `zenrows mcp` | MCP server config (remote + local) | **available** |
| Zenrows CLI | this repo | available |

Expand Down Expand Up @@ -117,7 +117,7 @@ zenrows extract <url> --output markdown

## 9. Batch (beta)

The Zenrows **Batch Scraper API** (`https://async.api.zenrows.com/v1`) fans a
Zenrows **Batch** (`https://async.api.zenrows.com/v1`) fans a
protected fetch/extract out over many URLs. It is a real product in
**beta**: the cloud subcommands work once your API key has
beta access; without it the API returns `BATCH_ACCESS_DENIED`. Local spec
Expand All @@ -138,8 +138,8 @@ locally or fan out with `zenrows fetch` per URL.

## 10. Browser Sessions

Escalation only, gated by `policy.allow_browser`. Backed by the Zenrows Scraping
Browser (CDP) and the `@zenrows/mcp` `browser_*` tools.
Escalation only, gated by `policy.allow_browser`. Backed by Zenrows Browser
Sessions (CDP) and the `@zenrows/mcp` `browser_*` tools.

## 11. MCP

Expand Down
15 changes: 4 additions & 11 deletions docs/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,8 @@ Classification is based on the public Zenrows documentation:

| Capability | Backend evidence | Status |
| --- | --- | --- |
| `protected_fetch` | Universal Scraper API `GET https://api.zenrows.com/v1/` with `mode`, `js_render`, `premium_proxy`, `proxy_country`, `wait`/`wait_for`, `js_instructions`, `response_type`, `screenshot`, `original_status`, … | available |
| `extract` | Same `/v1/` endpoint via `autoparse`, `css_extractor`, `response_type=markdown\|plaintext` | available |
| `batch` | Zenrows Batch Scraper API `https://async.api.zenrows.com/v1` (separate host, `X-API-Key` header) — real product in beta. Cloud subcommands (create/status/results/cancel/wait/retry-failed) work WITH beta access; without it the API returns 403 → `BATCH_ACCESS_DENIED`. Local JSONL spec validation + credit estimation work with no key. | beta |
| `browser` | Zenrows Scraping Browser (CDP) + `@zenrows/mcp` `browser_*` tools; no managed REST sessions API | experimental |
| `protected_fetch` | Fetch `GET https://api.zenrows.com/v1/` with `mode`, `js_render`, `premium_proxy`, `proxy_country`, `wait`/`wait_for`, `js_instructions`, `response_type`, `screenshot`, `original_status`, … | available |
| `extract` | Extract `GET https://api.zenrows.com/v1/` with `/v1/` endpoint via `extract`, `css_extractor`, `response_type=markdown\|plaintext` | available |
| `batch` | Batch `https://async.api.zenrows.com/v1` (separate host, `X-API-Key` header) — real product in beta. Cloud subcommands (create/status/results/cancel/wait/retry-failed) work WITH beta access; without it the API returns 403 → `BATCH_ACCESS_DENIED`. Local JSONL spec validation + credit estimation work with no key. | beta |
| `browser` | Zenrows Browser Sessions (CDP) + `@zenrows/mcp` `browser_*` tools; no managed REST sessions API | experimental |
| `mcp` | Hosted `https://mcp.zenrows.com/mcp` + local `npx -y @zenrows/mcp` | available |

## Important honesty note

`protected_fetch` and `extract` are the **same** product: a single `/v1/`
Universal Scraper API. "Extract" is not a separate endpoint — it is parameters
on that endpoint (`autoparse` / `css_extractor` / `response_type`). The CLI keeps
them as separate commands only for ergonomics.
28 changes: 14 additions & 14 deletions registry/capabilities.json
Original file line number Diff line number Diff line change
@@ -1,50 +1,50 @@
{
"$comment": "Honest capability matrix derived from confirmed Zenrows docs. Every command checks status here before attempting a cloud call. 'available' = a documented endpoint exists today; 'planned' = no documented endpoint yet (local spec / unavailable behavior only); 'experimental' = exists but gated behind policy.",
"$comment": "Capability matrix for the Zenrows CLI. Every command consults this file before attempting a cloud call, so the CLI never fakes behavior for primitives the backend does not expose. Status values and classification rationale live in docs/capabilities.md; entries must stay in sync with the Capability type in src/types/index.ts.",
"capabilities": {
"protected_fetch": {
"key": "protected_fetch",
"label": "Protected Fetch",
"status": "available",
"command": "zenrows fetch",
"backend": "GET https://api.zenrows.com/v1/",
"backend": "Fetch — GET https://api.zenrows.com/v1/",
"requiresAuth": true,
"notes": "Universal Scraper API. Confirmed params: mode=auto (Adaptive Stealth), js_render, premium_proxy, proxy_country, wait, wait_for, js_instructions, custom_headers, session_id, original_status, allowed_status_codes, block_resources, response_type, screenshot."
"notes": "Supported params: mode=auto (Adaptive Stealth), js_render, premium_proxy, proxy_country, wait, wait_for, js_instructions, custom_headers, session_id, original_status, allowed_status_codes, block_resources, response_type, screenshot."
},
"extract": {
"key": "extract",
"label": "Extract (Autoparse / CSS / Markdown)",
"status": "available",
"status": "beta",
"command": "zenrows extract",
"backend": "GET https://api.zenrows.com/v1/ (autoparse, css_extractor, response_type)",
"backend": "Extract — GET https://api.zenrows.com/v1/ (autoparse, css_extractor, response_type)",
"requiresAuth": true,
"notes": "Structured extraction runs on the same /v1/ endpoint via autoparse=true, css_extractor, and response_type=markdown|plaintext. There is no separate /extract endpoint."
"notes": "Structured extraction on the Extract /v1/ endpoint via extract=auto, css_extractor, and response_type=markdown|plaintext."
},
"batch": {
"key": "batch",
"label": "Batch (beta)",
"status": "beta",
"command": "zenrows batch",
"backend": "Batch Scraper API — https://async.api.zenrows.com/v1 (X-API-Key header; separate host from the scraper /v1/)",
"backend": "Batch — https://async.api.zenrows.com/v1 (X-API-Key header; separate host from the Fetch/Extract /v1/)",
"requiresAuth": true,
"notes": "The Zenrows Batch Scraper API is a real product in beta. The cloud subcommands (create/status/results/cancel/wait/retry-failed) work WITH beta access; without it the API returns 403 BATCH_ACCESS_DENIED. Local value always works with no key: `zenrows batch estimate` validates JSONL job specs and estimates credit cost."
"notes": "Batch is in beta. The cloud subcommands (create/status/results/cancel/wait/retry-failed) require beta access; without it the API returns 403 (BATCH_ACCESS_DENIED). `zenrows batch estimate` works locally with no API key: it validates JSONL job specs and estimates credit cost."
},
"browser": {
"key": "browser",
"label": "Browser (Scraping Browser)",
"status": "experimental",
"label": "Browser Sessions",
"status": "available",
"command": "zenrows browser",
"backend": "Scraping Browser (CDP) + @zenrows/mcp browser tools",
"backend": "Browser Sessions (CDP) + @zenrows/mcp browser tools",
"requiresAuth": true,
"notes": "Zenrows Scraping Browser and the @zenrows/mcp browser_* tools exist. There is no managed REST 'sessions' API in the public docs, so this is gated as experimental and escalation-only (policy.allow_browser=false by default)."
"notes": "Browser Sessions (CDP) and the @zenrows/mcp browser_* tools exist, but there is no managed REST sessions API in the public docs. Gated behind policy.allow_browser (off by default) and intended as an escalation path only."
},
"mcp": {
"key": "mcp",
"label": "MCP",
"status": "available",
"command": "zenrows mcp",
"backend": "remote https://mcp.zenrows.com/mcp + local npx -y @zenrows/mcp",
"backend": "Remote https://mcp.zenrows.com/mcp + local `npx -y @zenrows/mcp`",
"requiresAuth": true,
"notes": "Both a hosted remote MCP server and a local STDIO server (@zenrows/mcp, ZENROWS_API_KEY env) are documented."
"notes": "Hosted remote MCP server plus a local STDIO server (`npx -y @zenrows/mcp`, authenticated via the ZENROWS_API_KEY environment variable)."
}
}
}
4 changes: 2 additions & 2 deletions registry/skills.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
{
"name": "batch-jobs",
"type": "skill",
"description": "Scale protected fetch/extract over many URLs with the Batch Scraper API (beta): submit/track/collect jobs with beta access; validate + estimate specs locally with no key.",
"description": "Scale protected fetch/extract over many URLs with Batch (beta): submit/track/collect jobs with beta access; validate + estimate specs locally with no key.",
"status": "beta",
"requires_backend_capabilities": ["batch"],
"requires_auth": true,
Expand All @@ -47,7 +47,7 @@
{
"name": "interact-browser",
"type": "skill",
"description": "Escalate to a browser (Scraping Browser / MCP) only when fetch/extract cannot do the job.",
"description": "Escalate to a browser (Browser Sessions / MCP) only when fetch/extract cannot do the job.",
"status": "experimental",
"requires_backend_capabilities": ["browser"],
"requires_auth": true,
Expand Down
4 changes: 2 additions & 2 deletions registry/templates.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{
"name": "protected-fetch-node",
"type": "template",
"description": "Minimal Node.js project calling the Zenrows Universal Scraper API.",
"description": "Minimal Node.js project calling Zenrows Fetch.",
"status": "available",
"requires_backend_capabilities": ["protected_fetch"],
"requires_auth": true,
Expand All @@ -25,7 +25,7 @@
{
"name": "batch-jsonl-pipeline",
"type": "template",
"description": "JSONL job-spec scaffold for high-scale workloads on the Batch Scraper API: submit/track/collect with beta access, validate + estimate locally with no key (beta).",
"description": "JSONL job-spec scaffold for high-scale workloads on Batch: submit/track/collect with beta access, validate + estimate locally with no key (beta).",
"status": "beta",
"requires_backend_capabilities": [],
"requires_auth": false,
Expand Down
4 changes: 2 additions & 2 deletions skills/batch-jobs/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: batch-jobs
description: Scale protected fetch/extract over many URLs via the Batch Scraper API (beta). Cloud create/status/results/cancel/wait/retry-failed work with beta access; estimate/validate run locally with no key.
description: Scale protected fetch/extract over many URLs via Batch (beta). Cloud create/status/results/cancel/wait/retry-failed work with beta access; estimate/validate run locally with no key.
version: 0.1.0
requires_backend_capabilities: [batch]
---
Expand All @@ -11,7 +11,7 @@ Process large workloads reliably and asynchronously. Batch is where Zenrows'
high-scale anti-bot advantage becomes obvious — Zenrows wins when the workflow
runs over thousands, millions, or recurring sets of URLs.

> Status: **beta**. The Zenrows Batch Scraper API is a real
> Status: **beta**. The Zenrows **Batch** is a real
> product in beta and runs on a separate host
> (`async.api.zenrows.com/v1`). The cloud subcommands work once your account has
> beta access; without it the API returns 403 → `BATCH_ACCESS_DENIED`. The
Expand Down
2 changes: 1 addition & 1 deletion skills/cost-control/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ requires_backend_capabilities: []

Prefer the **cheapest reliable** configuration; escalate only with evidence.

## Cost multipliers (Universal Scraper API)
## Cost multipliers (Fetch)
- Basic request: **1×**
- JS rendering (`js_render`): **5×**
- Premium proxies (`premium_proxy`): **10×**
Expand Down
4 changes: 0 additions & 4 deletions skills/extract/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ requires_backend_capabilities: [extract]
Convert protected pages into structured data. The value is **protected page
access + extraction**, not generic LLM parsing.

> Honest note: there is no separate `/extract` endpoint. Extraction runs on the
> same Universal Scraper API (`/v1/`) via `autoparse`, `css_extractor`, and
> `response_type`.

## Methods (available today)
```
zenrows extract <url> --autoparse # automatic structured JSON
Expand Down
2 changes: 1 addition & 1 deletion skills/interact-browser/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ or Extract is insufficient.

> Status: **experimental**, gated behind `policy.allow_browser` (default false).
> There is no managed REST "sessions" API; browser workflows run through the
> Zenrows **Scraping Browser** (CDP — connect Playwright/Puppeteer) and the
> Zenrows **Browser Sessions** (CDP — connect Playwright/Puppeteer) and the
> `@zenrows/mcp` `browser_*` tools (navigate, click, fill, screenshot, …).

## Rules
Expand Down
4 changes: 2 additions & 2 deletions skills/protected-fetch/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ requires_backend_capabilities: [protected_fetch]
# Protected Fetch

Retrieve pages that normal fetch, generic scrapers, or naive browser tools
cannot. This is the **core primitive** — backed by the Zenrows Universal Scraper
API (`GET https://api.zenrows.com/v1/`).
cannot. This is the **core primitive** — backed by Zenrows **Fetch**
(`GET https://api.zenrows.com/v1/`).

## When to use
- You have a known URL and want its content (HTML, Markdown, text, or a PDF).
Expand Down
6 changes: 3 additions & 3 deletions skills/zenrows/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,14 @@ Run `zenrows status` for the live capability matrix. As of this toolkit:
| Protected Fetch | `zenrows fetch` | available (`GET /v1/`) |
| Extract (Autoparse/CSS/Markdown) | `zenrows extract` | available (same `/v1/`) |
| Batch | `zenrows batch` | beta (validate specs locally) |
| Browser | `zenrows browser` | experimental (Scraping Browser / MCP) |
| Browser | `zenrows browser` | experimental (Browser Sessions / MCP) |
| MCP | `zenrows mcp` | available (remote + local server) |

Protected Fetch and Extract are the same Universal Scraper API used two ways.
Protected Fetch and Extract are the same API (`GET /v1/`) used two ways.
The command consults the capability matrix before any cloud call — it never
fakes success.

## Cost model (Universal Scraper API)
## Cost model (Fetch and Extract)

Relative multipliers: basic **1×**, JS rendering **5×**, premium proxies **10×**,
both **25×**. `mode=auto` charges only for the configuration that succeeds.
Expand Down
4 changes: 2 additions & 2 deletions src/adapters/batch.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* Batch Jobs adapter.
*
* Status: `beta` — the Zenrows Batch Scraper API is in beta.
* Status: `beta` — the Zenrows Batch API is in beta.
* With beta access the cloud subcommands run for real (see `core/batch-api.ts`).
* Without access the API returns 403 → BATCH_ACCESS_DENIED. This adapter owns
* the local, no-network pieces: validating a JSONL job spec, estimating credit
Expand Down Expand Up @@ -176,7 +176,7 @@ function assertProxyCountryPremium(params: Record<string, unknown>, where: strin
code: "PARAM_PROXY_COUNTRY_REQUIRES_PREMIUM",
message: `proxy_country needs premium proxies (or mode=auto) — ${where}.`,
likely_cause:
"The Batch Scraper API only geolocates the proxy when premium_proxy=true, or in Adaptive Stealth Mode (mode=auto).",
"Batch only geolocates the proxy when premium_proxy=true, or in Adaptive Stealth Mode (mode=auto).",
next_action:
"Add premium_proxy (10x cost) alongside proxy_country, or set mode=auto — geo-targeting works there without the flag.",
suggested_commands: ["zenrows batch create jobs.jsonl --premium-proxy --proxy-country us"],
Expand Down
2 changes: 1 addition & 1 deletion src/adapters/extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Extract adapter.
*
* IMPORTANT (honest mapping): there is no separate `/extract` endpoint.
* Structured extraction runs on the same `/v1/` Universal Scraper API via:
* Structured extraction runs on the same `/v1/` Fetch and Extract API via:
* - autoparse=true → automatic structured JSON (available)
* - css_extractor=<json> → selector-based field extraction (available)
* - outputs=<filters> → built-in output filters → JSON (available)
Expand Down
4 changes: 2 additions & 2 deletions src/adapters/protected-fetch.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Protected Fetch adapter → Zenrows Universal Scraper API (`GET /v1/`).
* Protected Fetch adapter → Zenrows Fetch (`GET /v1/`).
*
* Maps toolkit options to confirmed API parameters and enforces the
* auto/manual contract: in Adaptive Stealth Mode (mode=auto), `js_render` and
Expand Down Expand Up @@ -34,7 +34,7 @@ export interface FetchOptions {
cssExtractor?: string;
autoparse?: boolean;
/**
* Comma-separated Universal Scraper API output filters (e.g. "emails,links",
* Comma-separated Fetch and Extract output filters (e.g. "emails,links",
* or "*" for all available fields). Returns structured JSON. Standalone: not combined with
* autoparse / css_extractor / response_type.
*/
Expand Down
4 changes: 2 additions & 2 deletions src/cli/commands/batch.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* `zenrows batch` — Batch Scraper API (status: beta).
* `zenrows batch` — Batch API (status: beta).
*
* Local (no key, always works): `estimate`/`create --dry-run`-style spec
* validation + credit estimate. Cloud (needs a key + Batch beta access):
Expand All @@ -18,7 +18,7 @@ import { printError, writeOut } from "../output.ts";

export const batch: Command = {
name: "batch",
summary: "Run JSONL batch jobs on the Zenrows Batch Scraper API (beta).",
summary: "Run JSONL batch jobs on Zenrows Batch (beta).",
usage: "zenrows batch <estimate|create <file.jsonl>|status <id>|results <id>|cancel <id>|wait <id>|retry-failed <id>>",
help: [
"Local (no key):",
Expand Down
Loading