Skip to content
Merged
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
22 changes: 19 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ that endpoint with extraction parameters, not a separate product.
| Command | What it does | Status (this build) |
| --- | --- | --- |
| `zenrows fetch` | Fetch: retrieve a protected page | **available**: `GET https://api.zenrows.com/v1/` |
| `zenrows extract` | Extract: turn a page into structured data with `extract=auto` on any prepared domain (falls back to Autoparse on one not prepared yet), CSS selectors, or Markdown | **available**: `GET https://api.zenrows.com/v1/?extract=auto` |
| `zenrows extract` | Extract: turn a page into structured data with `extract=auto` on any prepared domain (prepare one yourself with `zenrows extract prepare`), CSS selectors, or Markdown | **available**: `GET https://api.zenrows.com/v1/?extract=auto` |
| `zenrows batch` | Batch: run Fetch and Extract over many URLs in one job; specs validate and estimate locally with no key | **available**: `POST https://async.api.zenrows.com/v1/jobs` |
| `zenrows crawl` | Crawl (new): collect the URLs behind one start page, optionally with each page's HTML | **available**: `POST https://api.zenrows.com/v1/crawls` |
| `zenrows browser` | Browser Sessions: a real cloud browser over REST (same backend as MCP `browser_*`); escalation-only, billed by bandwidth + time | **available**: `POST https://mcp.zenrows.com/browser/sessions` |
Expand All @@ -56,7 +56,7 @@ npm install -g @zenrows/cli # puts `zenrows` on PATH (466 ms, zero deps)
zenrows init
zenrows fetch https://httpbin.io/html # auto-provisions a Free plan account on first use
zenrows extract https://www.owler.com/company/meltwater # extract=auto on a prepared domain
zenrows extract https://www.scrapingcourse.com/ecommerce/ --autoparse # Autoparse (any domain)
zenrows extract https://www.scrapingcourse.com/ecommerce/ --css '{"title":"h1"}' # CSS selectors (any domain)
```

> Prefer no global install? Prefix each command with `npx -y @zenrows/cli`
Expand Down Expand Up @@ -118,12 +118,28 @@ zenrows fetch <url> --proxy-country us --wait-for ".price"

```bash
zenrows extract https://www.owler.com/company/meltwater # extract=auto (prepared domain)
zenrows extract https://www.scrapingcourse.com/ecommerce/ --autoparse # Autoparse (any domain)
zenrows extract https://www.scrapingcourse.com/ecommerce/ --autoparse # deprecated Autoparse (any domain)
zenrows extract <url> --css '{"title":"h1","price":".price"}' --validate
zenrows extract <url> --outputs emails,links # built-in output filters → JSON
zenrows extract <url> --output markdown
```

On a domain Extract hasn't prepared yet, `zenrows extract` fails with
`EXTRACT_DOMAIN_NOT_PREPARED` (API code REQS007) and prints the command to
prepare it. Preparing takes a few minutes and costs no credits; each new domain
uses one slot of the monthly allowance. `EXTRACT_PAGE_NOT_RECOGNISED` (also
REQS007) is different: the domain is prepared, but this page is not one Extract
extracts. Try a product, article or listing page instead, retry it once in a
few minutes if it should work, or use `--css`; preparing again does not change
it. `zenrows extract` never falls back to Autoparse. The `fallbackAutoparse`
adapter option is deprecated and has no effect.

```bash
zenrows extract prepare example.com --url https://example.com/p/1 --url https://example.com/list --wait
zenrows extract prepare status example.com # progress + monthly allowance
zenrows extract https://example.com/p/1 # then extract again
```

## 9. Batch

Zenrows **Batch** (`https://async.api.zenrows.com/v1`) fans a
Expand Down
2 changes: 1 addition & 1 deletion agent-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Use MCP tool names — not CLI `zenrows fetch`:
| --- | --- |
| `zenrows` | Decision tree across all primitives |
| `protected-fetch` | `scrape` |
| `extract` | `extract` (fallback: `scrape` + autoparse / css_extractor) |
| `extract` | `extract` (fallback: `scrape` + css_extractor); on REQS007 with reason `domain_not_prepared`, `prepare_domain` then `prepared_domain_status` |
| `batch-jobs` | `batch_create`, `batch_status`, `batch_results`, `batch_cancel` |
| `interact-browser` | `browser_*` (escalation only) |
| `cost-control` | Cost multipliers + escalate-with-evidence |
Expand Down
4 changes: 2 additions & 2 deletions agent-plugin/skills/cost-control/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ These multipliers apply when you enable the flags (on `scrape` or `extract`):
- Both: **25×**

`extract` `mode=auto` is currently free; billing may apply
later. Do not promise permanent free extract pricing. Autoparse / CSS still
follow the multipliers above when stealth flags are set.
later. Do not promise permanent free extract pricing. CSS (and the deprecated
Autoparse) still follow the multipliers above when stealth flags are set.

## Browser sessions (`browser_*`)

Expand Down
58 changes: 49 additions & 9 deletions agent-plugin/skills/extract/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,67 @@ Convert protected pages into structured data. Value is **protected page access
Use **`extract`** when you need structured JSON fields rather than a full page:

```
extract({ url }) # mode=auto (default)
extract({ url, mode: "autoparse" }) # general Autoparse (any domain)
extract({ url }) # mode=auto (default), returns `parsed` only
extract({ url, include_html: true }) # also return the full page HTML
extract({ url, mode: "autoparse" }) # deprecated Autoparse (any domain)
extract({ url, mode: "css", css_extractor: '{"title":"h1","price":".price"}' })
extract({ url, proxy_country: "US" }) # geo-targeted
```

`mode=auto` (`extract=auto`) works on any domain Zenrows has prepared, with any
key, and is currently free; billing may apply later. If the
domain is not enabled (AUTH010) or not prepared yet (REQS007), the tool retries
once with Autoparse by default (`fallback_autoparse`).
`mode=auto` (`extract=auto`) works on any prepared domain, with any key, and
is currently free; billing may apply later. The result carries `parsed` only;
the page HTML is most of the response size, so ask for it with
`include_html: true` only when you need it.

Every mode uses Adaptive Stealth Mode by default: pass just the URL, even for
protected or dynamic sites. Set `js_render` or `premium_proxy` only to force a
fixed configuration; that turns Adaptive Stealth Mode off. Check `empty` in the
result: true means no field came back with a value.

## REQS007: prepare the domain, then extract again

`mode=auto` works on any prepared domain, and you can prepare one yourself.
When Extract refuses a page, `extract` returns a tool error with code
`REQS007` (legacy `AUTH010`) and a `reason`. It never retries with Autoparse.
`fallback_autoparse` is deprecated and has no effect; don't send it.

| `reason` | Meaning | Next step |
| --- | --- | --- |
| `domain_not_prepared` | The domain is not prepared yet. | `prepare_domain` (the error's `next_tool` holds its arguments), then poll, then `extract` again. |
| `page_not_recognised` | The domain is prepared, but Extract did not recognise this page as one it can extract (a page with nothing to extract, such as a home page, or a transient failure). | Try a product, article or listing page on the same site instead. If this page should work, retry it once in a few minutes; do not retry in a loop. Otherwise use `mode=css`. Do not call `prepare_domain`: preparing again does not change this. |
| `domain_not_enabled` | Extract is not enabled for the site. | Use `mode=css`, or contact Zenrows support to have the site enabled. Preparing does not change this. |

Preparing a domain:

```
extract({ url }) # 1. REQS007, reason domain_not_prepared
prepare_domain({ domain: "example.com",
example_urls: ["https://example.com/p/1", "https://example.com/list"] })
# 2. returns at once, status "preparing"
prepared_domain_status({ domain: "example.com" }) # 3. about every 30 seconds until "ready"
extract({ url }) # 4. extract again
```

- `example_urls`: 1 to 5 real pages you want to extract, one per page kind
(for example a product page and a listing page).
- The domain is the host: `shop.example.com` and `example.com` are prepared
separately, and a leading `www.` is dropped.
- Preparing takes a few minutes and costs no credits. Each new domain uses one
slot of the account's monthly allowance; `prepared_domain_status` returns it
(`used` / `limit` / `remaining`) and a `next` field with what to do now.
- `out_of_allowance`: no slots left this month; use `mode=css`.
`overloaded` / `disabled`: retry in a few minutes; do not loop.
A `failed` status carries `failure.code` and `failure.slot_consumed`; follow `next`.
- A domain that is already prepared answers `ready` at once
(`already_prepared: true`) and uses no slot: call `extract`. If it then
returns `page_not_recognised`, follow that row of the table, not `prepare_domain`.

## Fallback: `scrape` extraction params

If `extract` is unavailable, similar outcomes are available on **`scrape`**:

```
scrape({ url, autoparse: true })
scrape({ url, autoparse: true }) # deprecated
scrape({ url, css_extractor: '{"title":"h1","price":".price"}' })
scrape({ url, outputs: "emails,links" }) # or "*" for all built-ins
```
Expand All @@ -46,7 +85,8 @@ scrape({ url, outputs: "emails,links" }) # or "*" for all built-ins
## When to use which

- **`mode=auto`**: site-tailored fields on any prepared domain.
- **`mode=autoparse`**: any website; also the automatic AUTH010 / REQS007 fallback.
- **`mode=autoparse`** (deprecated): any website, only when asked for. Do not
build new work on it.
- **`mode=css`**: known fields/selectors; deterministic.
- **Markdown via `scrape`**: feed clean content to an LLM yourself.

Expand All @@ -57,7 +97,7 @@ Stealth flags (`js_render`, `premium_proxy`) still apply the usual multipliers
## Rules

- Validate on a single page before `batch_create` ([[batch-jobs]]).
- If Autoparse misses fields, switch to `css_extractor`, or add `js_render`
- If a field is missing, switch to `css_extractor`, or add `js_render`
for JS-heavy pages.
- Prefer MCP `extract` / `scrape` over CLI `zenrows extract` when the MCP
server is connected.
Expand Down
2 changes: 1 addition & 1 deletion agent-plugin/skills/protected-fetch/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ scrape({ url, js_render: true, wait_for: ".price" })
- Prefer `response_type: "markdown"` for LLM consumption; use `html` only when
you need raw markup.
- For **structured** fields, prefer the `extract` tool (or `scrape` with
`autoparse` / `css_extractor` / `outputs`) — see [[extract]].
`css_extractor` / `outputs`) — see [[extract]].
- On failure, escalate with evidence before `browser_*` ([[interact-browser]]).
- Cost model: [[cost-control]].

Expand Down
5 changes: 3 additions & 2 deletions agent-plugin/skills/zenrows/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ If the user has a known URL and wants page content:

If the user has a known URL and wants structured data:
→ Use Extract. (MCP tool: extract)
Fallback: scrape with autoparse / css_extractor / outputs
Fallback: scrape with css_extractor / outputs

If the user has many URLs (bulk):
→ Use Batch. (validate one page with scrape/extract first,
Expand Down Expand Up @@ -67,7 +67,8 @@ If the user needs login, clicks, forms, sessions, or persistent state:
| Primitive | MCP tools | Notes |
| --- | --- | --- |
| Protected Fetch | `scrape` | Default markdown; HTML / plaintext / PDF / screenshot options |
| Extract | `extract` | Structured JSON (`extract=auto`, autoparse, css_extractor). `extract=auto` is currently free; billing may apply later |
| Extract | `extract` | Structured JSON (`extract=auto` on any prepared domain, css_extractor; autoparse deprecated). `extract=auto` is currently free; billing may apply later |
| Prepare a domain | `prepare_domain`, `prepared_domain_status` | After an `extract` REQS007 with reason `domain_not_prepared`: prepare the domain with 1 to 5 example URLs, poll about every 30 seconds until ready, extract again. No credits; uses one monthly allowance slot. Not for `page_not_recognised`: that domain is already prepared |
| Batch | `batch_create`, `batch_status`, `batch_results`, `batch_cancel` | Same credits as Fetch; a 403 maps to `BATCH_ACCESS_DENIED` |
| Browser | `browser_*` (start with `browser_navigate`) | Escalation-only; same backend as Browser Sessions |

Expand Down
2 changes: 1 addition & 1 deletion docs/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Classification is based on the public Zenrows documentation:
| Capability | Backend evidence | Status |
| --- | --- | --- |
| `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/` via `extract=auto` (any prepared domain; CLI falls back to `autoparse` on one not prepared yet), plus `autoparse`, `css_extractor`, `outputs`, `response_type=markdown\|plaintext` | available |
| `extract` | Extract `GET https://api.zenrows.com/v1/` via `extract=auto` (any prepared domain; on one not prepared yet the CLI reports REQS007 as `EXTRACT_DOMAIN_NOT_PREPARED` with the command to prepare it: `zenrows extract prepare <domain> --url <page>`, which calls `POST /v1/extract/prepared-domains`; a page Extract did not recognise on a prepared domain is `EXTRACT_PAGE_NOT_RECOGNISED`, which preparing does not change), plus deprecated `autoparse`, `css_extractor`, `outputs`, `response_type=markdown\|plaintext` | available |
| `batch` | Batch `https://async.api.zenrows.com/v1` (separate host, `X-API-Key` header). Local JSONL spec validation + credit estimation work with no key. | available |
| `crawl` | Crawl `https://api.zenrows.com/v1/crawls` (`X-API-Key` header; same host as Fetch/Extract). Follows the links on each page from one start URL, up to `depth` when set (optional; no depth limit when omitted), on the start URL's registrable domain (subdomains count); returns URLs, and with `output_format: "html"` each page's HTML. Subcommands: `create`, `get`, `list`, `results`, `content`, `download`, `stop`, `wait`. Without Crawl access the API returns 403 `REQS008` → `CRAWL_NOT_ENABLED`. | available |
| `browser` | Browser Sessions REST API `https://mcp.zenrows.com/browser/sessions/*` (Bearer; same backend as `@zenrows/mcp` `browser_*`); CDP via `zenrows browser connect`. Escalation-only (prefer fetch/extract); on by default, opt out via `policy.allow_browser`; bills by bandwidth + session time (15-min max) | available |
Expand Down
2 changes: 1 addition & 1 deletion evals/extract-smoke/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Reproducible smoke test for Extract (`extract=auto`).

- **Target:** `https://www.owler.com/company/meltwater` (enabled domain, real company page)
- **Target:** `https://www.owler.com/company/meltwater` (prepared domain, real company page)
- **Config:** `extract=auto`
- **Success criteria:** HTTP 2xx and a non-empty body
- **Cost:** ~1× basic request
Expand Down
6 changes: 3 additions & 3 deletions registry/capabilities.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
},
"extract": {
"key": "extract",
"label": "Extract (extract=auto / Autoparse / CSS / Markdown)",
"label": "Extract (extract=auto / CSS / Markdown)",
"status": "available",
"command": "zenrows extract",
"backend": "Extract — GET https://api.zenrows.com/v1/ (extract=auto, autoparse, css_extractor, outputs, response_type)",
"backend": "Extract — GET https://api.zenrows.com/v1/ (extract=auto, css_extractor, outputs, response_type; autoparse deprecated)",
"requiresAuth": true,
"notes": "Default is extract=auto, which works on any prepared domain with any key; CLI falls back to autoparse on REQS007 (domain not prepared yet) or AUTH010. Autoparse / CSS / outputs / markdown work on any domain."
"notes": "Default is extract=auto, which works on any prepared domain with any key. A REQS007 (legacy AUTH010) is reported, never retried, with the next step for its reason: on a domain not prepared yet (EXTRACT_DOMAIN_NOT_PREPARED), prepare it with `zenrows extract prepare <domain> --url <page> [--wait]` (POST /v1/extract/prepared-domains: 1 to 5 example URLs, a few minutes, no credits) and check it with `zenrows extract prepare status <domain>`; on a page Extract did not recognise on a prepared domain (EXTRACT_PAGE_NOT_RECOGNISED), try a product, article or listing page, retry it once in a few minutes, or use --css, because preparing again does not change it. CSS / outputs / markdown work on any domain; autoparse is deprecated and runs only with --autoparse."
},
"batch": {
"key": "batch",
Expand Down
4 changes: 2 additions & 2 deletions registry/skills.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@
{
"name": "extract",
"type": "skill",
"description": "Turn protected pages into structured data with Autoparse / CSS / Markdown.",
"description": "Turn protected pages into structured data with Extract / CSS / Markdown (Autoparse is deprecated).",
"status": "available",
"requires_backend_capabilities": ["extract"],
"requires_auth": true,
"version": "0.1.0",
"path": "skills/extract",
"tags": ["extract", "autoparse", "structured-data"]
"tags": ["extract", "structured-data"]
},
{
"name": "batch-jobs",
Expand Down
2 changes: 1 addition & 1 deletion registry/templates.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
{
"name": "extract-pricing-pages",
"type": "template",
"description": "Extract structured pricing fields from product pages (Autoparse / CSS).",
"description": "Extract structured pricing fields from product pages (extract=auto / CSS).",
"status": "available",
"requires_backend_capabilities": ["extract"],
"requires_auth": true,
Expand Down
Loading
Loading