@zenrows/cli 1.4.2 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -37,8 +37,9 @@ that endpoint with extraction parameters, not a separate product.
37
37
  | Command | What it does | Status (this build) |
38
38
  | --- | --- | --- |
39
39
  | `zenrows fetch` | Fetch — retrieve a protected page | **available** — `GET https://api.zenrows.com/v1/` |
40
- | `zenrows extract` | Extract — `extract=auto` (domain-gated open beta; falls back to Autoparse) / CSS / Markdown | **beta** — same `/v1/` |
41
- | `zenrows batch` | Batch — fan out over many URLs | beta — cloud works with beta access; local validate/estimate always |
40
+ | `zenrows extract` | Extract: `extract=auto` (any prepared domain; falls back to Autoparse on one not prepared yet), CSS, or Markdown | available, same `/v1/` |
41
+ | `zenrows batch` | Batch: fan out over many URLs | **available**: `https://async.api.zenrows.com/v1`; local validate/estimate need no key |
42
+ | `zenrows crawl` | Crawl (new) — collect the URLs behind one start page, optionally with each page's HTML | **available** — `https://api.zenrows.com/v1/crawls` |
42
43
  | `zenrows browser` | Browser Sessions REST API (same backend as MCP `browser_*`) | **available** — escalation-only; bills by bandwidth + time |
43
44
  | `zenrows mcp` | MCP server config (remote + local) | **available** |
44
45
  | Zenrows CLI | this repo | available |
@@ -122,12 +123,11 @@ zenrows extract <url> --outputs emails,links # built-
122
123
  zenrows extract <url> --output markdown
123
124
  ```
124
125
 
125
- ## 9. Batch (beta)
126
+ ## 9. Batch
126
127
 
127
128
  Zenrows **Batch** (`https://async.api.zenrows.com/v1`) fans a
128
- protected fetch/extract out over many URLs. It is a real product in
129
- **beta**: the cloud subcommands work once your API key has
130
- beta access; without it the API returns `BATCH_ACCESS_DENIED`. Local spec
129
+ protected fetch/extract out over many URLs. The cloud subcommands work
130
+ with any active API key and spend the same credits as Fetch. Local spec
131
131
  validation + credit estimation always work (no key needed).
132
132
 
133
133
  ```bash
@@ -140,8 +140,7 @@ zenrows batch cancel <job-id> # stop an in-flight run
140
140
 
141
141
  `jobs.jsonl` is one JSON object per line, each with a `url` (plus optional
142
142
  per-line overrides like `js_render`, `premium_proxy`, `proxy_country`,
143
- `extract`). Request beta access from Zenrows to run in the cloud; until then,
144
- validate/estimate locally or fan out with `zenrows fetch` per URL.
143
+ `extract`). Validate and estimate locally first, then submit with `create`.
145
144
 
146
145
  **Extract in Batch.** Set `"extract": "auto"` on a line to run that URL through
147
146
  Extract — structured data instead of raw HTML:
@@ -156,7 +155,79 @@ An Extract task costs the same as a regular one (1 credit at base tier), so
156
155
  keys: `html` (the raw page) and `parsed` (the structured data) — validate a
157
156
  sample with `zenrows extract <url>` before running the full batch.
158
157
 
159
- ## 10. Browser Sessions
158
+ ## 10. Crawl (new)
159
+
160
+ Crawl is still evolving: new features are coming, limits may be tuned, and the changelog announces each change.
161
+
162
+ Crawl (`https://api.zenrows.com/v1/crawls`)
163
+ takes one start URL and returns the URLs it finds behind it, optionally with
164
+ each page's HTML. Use it when you know the listing or section but not the item
165
+ URLs. Crawl follows the links on each page, up to `--depth` hops when you set
166
+ it, and stays on the start URL's registrable domain (subdomains count). Without
167
+ `--output-format html` it returns URLs only. An account without Crawl access gets
168
+ `CRAWL_NOT_ENABLED` ("Crawl is not enabled for this account", HTTP 403
169
+ `REQS008`).
170
+
171
+ ```bash
172
+ zenrows crawl create https://example.com/products/ --depth 1 \
173
+ --max-items 20 --include-patterns /product/ --follow # create and wait for the end
174
+ zenrows crawl results <crawl-id> --out urls.jsonl # every kept URL (paginated)
175
+ zenrows crawl create <url> --depth 2 --output-format html # also fetch each kept page's HTML
176
+ zenrows crawl content <crawl-id> <content-id> # one page's HTML
177
+ zenrows crawl download <crawl-id> # NDJSON export (URLs + HTML) → <crawl-id>.jsonl
178
+ zenrows crawl get <crawl-id> # status, coverage, stop reason, one page of results
179
+ zenrows crawl wait <crawl-id> # wait for the end
180
+ zenrows crawl list # your crawls, newest first
181
+ zenrows crawl stop <crawl-id> # stop a running crawl
182
+ ```
183
+
184
+ `--depth` (link hops, 1 to 100000) is optional: leave it out and the crawl
185
+ follows links until `--max-items` or `--max-pages` stops it, or nothing is left
186
+ to open. `--max-items` and `--max-pages` cap the crawl (API default 100 each),
187
+ and `--max-pages` bounds the cost, since each page is one fetch. The local policy `max_pages_per_run` caps `--max-pages` (default
188
+ 1000). `--output-format html` uses up the page budget: each kept page is one
189
+ more fetch, and `--max-pages` counts it. `--include-patterns` /
190
+ `--exclude-patterns` are repeatable substring filters on the URL. `get` and
191
+ `list` read one page and take `--cursor` and `--limit`. `results` follows every
192
+ page (`--limit` is the page size); on a running crawl it returns the URLs kept
193
+ so far, with `partial: true`. `download` reports the crawl's `status` (`null`
194
+ when the API sends none), and `partial: true` while it runs.
195
+
196
+ With `--json`, `create`, `get`, `wait` and `stop` print
197
+ `{ok, crawl_id, status, crawl}`. The crawl object (with `coverage`,
198
+ `stop_reason` and `error`) is under `crawl`. `get` adds `results` and
199
+ `next_cursor`. All keys are snake_case.
200
+
201
+ `wait`, and `create --follow`, poll until the crawl ends or `--timeout` seconds
202
+ run out (default 600). When the time runs out, the command exits 0 and prints
203
+ the crawl with status `running` and the hint `zenrows crawl wait <crawl-id>`
204
+ (under `--json`, in `note`); the crawl keeps running. Ctrl-C stops the wait, not the crawl, and exits 130;
205
+ resume with `zenrows crawl wait <crawl-id>`. A crawl that ends `failed` exits 1
206
+ with `CRAWL_FAILED`.
207
+
208
+ Errors carry the API's code as `server_code`, `crawl_id` on any call about one
209
+ crawl, and `retry_after` on a 429. When the account has reached its limit of
210
+ active jobs (3 by default), shared with its Batch jobs, a create gets 429
211
+ `too_many_crawls`, mapped to `CRAWL_TOO_MANY_CRAWLS`, and creates nothing; retry
212
+ after `retry_after` seconds. Any other 429 is `CRAWL_RATE_LIMITED`: requests
213
+ are being throttled; retry after `retry_after` seconds. Other codes: `CRAWL_INSUFFICIENT_CREDITS` and `CRAWL_KEY_CAP_REACHED` (402, with the
214
+ same claim, top-up and key-cap advice as other commands),
215
+ `CRAWL_INVALID_REQUEST` (400/422, do not retry as is), `CRAWL_REQUEST_IN_FLIGHT`
216
+ (409, retry once the first request ends), `CRAWL_NOT_FOUND`,
217
+ `CRAWL_CONTENT_NOT_FOUND`, `CRAWL_BUSY` (503 on a stop that was not saved yet;
218
+ run it again, it is safe) and `CRAWL_FAILED`. Set `ZENROWS_CRAWL_API_BASE` to
219
+ point the CLI at another deployment.
220
+
221
+ The CLI retries transient failures, as the Crawl SDKs do: up to 3 retries,
222
+ waiting the `Retry-After` seconds (at most 30) or 250 ms x 2^n (+/-20% jitter,
223
+ at most 10 s).
224
+ Reads retry on 429, 502, 503, 504 and network errors (a timeout counts).
225
+ `create` retries only with `--idempotency-key`, and only on 502, 503, 504 and
226
+ network errors, never on 429. `stop` retries on 429, 502, 503 (a `crawl_busy`
227
+ means the stop was not saved and the crawl still runs) and 504, waiting the
228
+ `Retry-After` seconds (at most 30) or 1 s; repeating a stop is safe.
229
+
230
+ ## 11. Browser Sessions
160
231
 
161
232
  Escalation only — **prefer `fetch`/`extract` for the vast majority of cases**;
162
233
  they cost less. Use the browser for logins, forms, and multi-step JS flows that
@@ -170,7 +241,7 @@ bring-your-own Playwright/Puppeteer.
170
241
  close interactive sessions with `zenrows browser close`. On by default; opt out
171
242
  with `zenrows policy set allow_browser false`.
172
243
 
173
- ## 11. MCP
244
+ ## 12. MCP
174
245
 
175
246
  ```bash
176
247
  zenrows mcp status
@@ -188,7 +259,7 @@ both servers in `mcp.json` (no secrets) and ships MCP-native skills under
188
259
  `agent-plugin/skills/`. It is included in the published `@zenrows/cli` npm
189
260
  tarball (`node_modules/@zenrows/cli/agent-plugin`).
190
261
 
191
- ## 12. Plugins
262
+ ## 13. Plugins
192
263
 
193
264
  ```bash
194
265
  zenrows plugin list
@@ -201,7 +272,7 @@ MCP-native skills). See [`agent-plugin/README.md`](agent-plugin/README.md).
201
272
  Legacy per-client snippets from `zenrows plugin install` remain available.
202
273
  CLI-oriented skills stay under repo-root `skills/`.
203
274
 
204
- ## 13. Skills
275
+ ## 14. Skills
205
276
 
206
277
  Agent-readable playbooks that teach agents how to choose primitives.
207
278
 
@@ -213,28 +284,28 @@ zenrows skill validate zenrows
213
284
 
214
285
  The master skill `skills/zenrows/SKILL.md` teaches the full decision tree.
215
286
 
216
- ## 14. Templates
287
+ ## 15. Templates
217
288
 
218
289
  ```bash
219
290
  zenrows template list
220
291
  zenrows template create protected-fetch-node --output ./my-project
221
292
  ```
222
293
 
223
- ## 15. Workflows
294
+ ## 16. Workflows
224
295
 
225
296
  ```bash
226
297
  zenrows workflow list
227
298
  zenrows workflow explain competitor-intelligence
228
299
  ```
229
300
 
230
- ## 16. Recipes
301
+ ## 17. Recipes
231
302
 
232
303
  ```bash
233
304
  zenrows recipe list
234
305
  zenrows recipe run fetch-protected-page
235
306
  ```
236
307
 
237
- ## 17. Evals
308
+ ## 18. Evals
238
309
 
239
310
  Reproducible, transparent benchmarks. **No competitor keys are bundled** and
240
311
  nothing is hardcoded to make Zenrows win — comparison evals require you to supply
@@ -249,7 +320,7 @@ zenrows eval report protected-fetch-smoke
249
320
  Reports write `input.json`, `results.json`, `report.md`, `failures.jsonl`,
250
321
  `cost.json`, and `traces/` under `.zenrows/evals/<run-id>/`.
251
322
 
252
- ## 18. Security
323
+ ## 19. Security
253
324
 
254
325
  - API keys are never printed or written into run artifacts/assets.
255
326
  - Secrets live in `.zenrows/secrets.json` (0600, gitignored); logs are redacted.
@@ -259,12 +330,12 @@ Reports write `input.json`, `results.json`, `report.md`, `failures.jsonl`,
259
330
  - Destructive `uninstall` requires `--yes`. Browser is on by default; opt out with
260
331
  `zenrows policy set allow_browser false`. Experimental features are off by default.
261
332
 
262
- ## 19. Capability matrix
333
+ ## 20. Capability matrix
263
334
 
264
335
  See [`registry/capabilities.json`](registry/capabilities.json). `zenrows status
265
336
  --json` emits it.
266
337
 
267
- ## 20. Contributing
338
+ ## 21. Contributing
268
339
 
269
340
  The CLI is TypeScript with **zero runtime dependencies** (native `fetch`,
270
341
  `node:util` `parseArgs`, `node:test`). Node 20+ runs the published build;
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: batch-jobs
3
- description: Scale protected fetch/extract over many URLs via Zenrows MCP batch_* tools (beta).
3
+ description: Scale protected fetch/extract over many URLs via Zenrows MCP batch_* tools.
4
4
  version: 0.1.0
5
5
  ---
6
6
 
@@ -9,8 +9,8 @@ version: 0.1.0
9
9
  Process large workloads asynchronously. Batch is where Zenrows' high-scale
10
10
  anti-bot advantage becomes obvious — thousands to millions of URLs.
11
11
 
12
- > Status: **beta**. Cloud Batch lives on `async.api.zenrows.com/v1`. Without
13
- > beta access the API returns `BATCH_ACCESS_DENIED`. Do not fake a successful
12
+ > Cloud Batch lives on `async.api.zenrows.com/v1`. A 403 maps to
13
+ > `BATCH_ACCESS_DENIED`. Do not fake a successful
14
14
  > run. This is **not** `browser_batch` (that batches browser actions in one
15
15
  > session).
16
16
 
@@ -18,7 +18,7 @@ These multipliers apply when you enable the flags (on `scrape` or `extract`):
18
18
  - Premium proxies (`premium_proxy`): **10×**
19
19
  - Both: **25×**
20
20
 
21
- `extract` `mode=auto` is **open beta** and currently free; billing may apply
21
+ `extract` `mode=auto` is currently free; billing may apply
22
22
  later. Do not promise permanent free extract pricing. Autoparse / CSS still
23
23
  follow the multipliers above when stealth flags are set.
24
24
 
@@ -32,7 +32,6 @@ Always call `browser_close`. Prefer `scrape` / `extract` when they can do the jo
32
32
 
33
33
  Batch is usually better at **scale** (many URLs) than fan-out of per-URL tool
34
34
  calls. Validate one URL with `scrape`/`extract` first, then `batch_create`.
35
- Batch access may be beta-gated (`BATCH_ACCESS_DENIED`).
36
35
 
37
36
  ## Tactics
38
37
 
@@ -20,10 +20,10 @@ extract({ url, mode: "css", css_extractor: '{"title":"h1","price":".price"}' })
20
20
  extract({ url, proxy_country: "US" }) # geo-targeted
21
21
  ```
22
22
 
23
- `mode=auto` (`extract=auto`) is **open beta**: richest on enabled domains,
24
- currently free; billing may apply later. If the domain is not enabled (AUTH010)
25
- or not prepared yet (REQS007), the tool retries once with Autoparse by default
26
- (`fallback_autoparse`).
23
+ `mode=auto` (`extract=auto`) works on any domain Zenrows has prepared, with any
24
+ key, and is currently free; billing may apply later. If the
25
+ domain is not enabled (AUTH010) or not prepared yet (REQS007), the tool retries
26
+ once with Autoparse by default (`fallback_autoparse`).
27
27
 
28
28
  Every mode uses Adaptive Stealth Mode by default: pass just the URL, even for
29
29
  protected or dynamic sites. Set `js_render` or `premium_proxy` only to force a
@@ -45,7 +45,7 @@ scrape({ url, outputs: "emails,links" }) # or "*" for all built-ins
45
45
 
46
46
  ## When to use which
47
47
 
48
- - **`mode=auto`**: site-tailored fields on beta-enabled domains.
48
+ - **`mode=auto`**: site-tailored fields on any prepared domain.
49
49
  - **`mode=autoparse`**: any website; also the automatic AUTH010 / REQS007 fallback.
50
50
  - **`mode=css`**: known fields/selectors; deterministic.
51
51
  - **Markdown via `scrape`**: feed clean content to an LLM yourself.
@@ -42,7 +42,7 @@ If the user has many URLs (bulk):
42
42
  → Use Batch. (validate one page with scrape/extract first,
43
43
  then batch_create → batch_status → batch_results)
44
44
  Prefer Batch over fan-out of scrape/extract
45
- per URL for bulk work (may be beta-gated).
45
+ per URL for bulk work.
46
46
  Cancel with batch_cancel when needed.
47
47
 
48
48
  If the user needs login, clicks, forms, sessions, or persistent state:
@@ -66,8 +66,8 @@ If the user needs login, clicks, forms, sessions, or persistent state:
66
66
  | Primitive | MCP tools | Notes |
67
67
  | --- | --- | --- |
68
68
  | Protected Fetch | `scrape` | Default markdown; HTML / plaintext / PDF / screenshot options |
69
- | Extract | `extract` | Structured JSON (`extract=auto`, autoparse, css_extractor). `extract=auto` open beta (currently free; billing may apply later) |
70
- | Batch | `batch_create`, `batch_status`, `batch_results`, `batch_cancel` | Beta; may return `BATCH_ACCESS_DENIED` without access |
69
+ | Extract | `extract` | Structured JSON (`extract=auto`, autoparse, css_extractor). `extract=auto` is currently free; billing may apply later |
70
+ | Batch | `batch_create`, `batch_status`, `batch_results`, `batch_cancel` | Same credits as Fetch; a 403 maps to `BATCH_ACCESS_DENIED` |
71
71
  | Browser | `browser_*` (start with `browser_navigate`) | Escalation-only; same backend as Browser Sessions |
72
72
 
73
73
  ## Cost notes
@@ -1,9 +1,8 @@
1
1
  /**
2
2
  * Batch Jobs adapter.
3
3
  *
4
- * Status: `beta` — the Zenrows Batch API is in beta.
5
- * With beta access the cloud subcommands run for real (see `core/batch-api.ts`).
6
- * Without access the API returns 403 → BATCH_ACCESS_DENIED. This adapter owns
4
+ * Status: `available`. The cloud subcommands call the Batch API with an API key
5
+ * (see `core/batch-api.ts`); a 403 maps to BATCH_ACCESS_DENIED. This adapter owns
7
6
  * the local, no-network pieces: validating a JSONL job spec, estimating credit
8
7
  * cost, and mapping the validated spec to the Batch API request body.
9
8
  */
@@ -55,6 +55,18 @@ async function runExtractOnce(opts, config, policy, apiKey) {
55
55
  output: method === "markdown" ? "markdown" : method === "plaintext" ? "plaintext" : "html",
56
56
  };
57
57
  const outcome = await runFetch(fetchOpts, config, policy, apiKey);
58
+ if (outcome.result.noDataExtracted) {
59
+ if (opts.validate) {
60
+ throw new ToolkitError({
61
+ code: "EXTRACT_VALIDATION_FAILED",
62
+ message: "Extract found no data on this page.",
63
+ likely_cause: "Extract knows this kind of page, but none of the fields came back filled (HTTP 422 no_data_extracted). The request was not charged.",
64
+ next_action: "Check that the page rendered fully: try --manual --js-render with --wait-for <selector>. Protected sites may also need --premium-proxy.",
65
+ suggested_commands: [`zenrows extract ${opts.url} --manual --js-render --premium-proxy`],
66
+ });
67
+ }
68
+ return { ...outcome, method, empty: true, noDataExtracted: true };
69
+ }
58
70
  const { data, html, empty } = parseExtractBody(method, outcome.result.body, opts);
59
71
  return { ...outcome, method, data, html, empty };
60
72
  }
@@ -80,7 +92,7 @@ function parseExtractBody(method, body, opts) {
80
92
  }
81
93
  let data = parsed;
82
94
  let html;
83
- // extract=auto beta shape: { parsed, html }. Prefer `parsed` for callers.
95
+ // extract=auto shape: { parsed, html }. Prefer `parsed` for callers.
84
96
  if (method === "extract" && parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
85
97
  const envelope = parsed;
86
98
  if ("parsed" in envelope) {
@@ -1,10 +1,10 @@
1
1
  /**
2
- * `zenrows batch` — Batch API (status: beta).
2
+ * `zenrows batch`: Batch API (status: available).
3
3
  *
4
4
  * Local (no key, always works): `estimate`/`create --dry-run`-style spec
5
- * validation + credit estimate. Cloud (needs a key + Batch beta access):
6
- * `create`, `status`, `results`, `cancel`, `wait`, `retry-failed`. Without beta
7
- * access the API returns 403 → BATCH_ACCESS_DENIED.
5
+ * validation + credit estimate. Cloud (needs a key):
6
+ * `create`, `status`, `results`, `cancel`, `wait`, `retry-failed`. A 403 maps to
7
+ * BATCH_ACCESS_DENIED.
8
8
  */
9
9
  import { statSync } from "node:fs";
10
10
  import { join } from "node:path";
@@ -22,12 +22,12 @@ import { ToolkitError, isKeyCapReached, keyCapReached } from "../../core/errors.
22
22
  import { printError, writeOut } from "../output.js";
23
23
  export const batch = {
24
24
  name: "batch",
25
- summary: "Run JSONL batch jobs on Zenrows Batch (beta).",
25
+ summary: "Run JSONL batch jobs on Zenrows Batch.",
26
26
  usage: "zenrows batch <estimate|create <file.jsonl>|status <id>|results <id>|cancel <id>|wait <id>|retry-failed <id>>",
27
27
  help: [
28
28
  "Local (no key):",
29
29
  " estimate <file.jsonl> [--manual] validate the spec + estimate credits (upper bound)",
30
- "Cloud (needs a key + Batch beta access):",
30
+ "Cloud (needs a key):",
31
31
  " create <file.jsonl> [flags] validate, then submit the job",
32
32
  " (default) Adaptive Stealth Mode (mode=auto) on every task that",
33
33
  " doesn't force js_render / premium_proxy itself",