@zenrows/cli 1.7.0 → 1.8.0

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.
@@ -14,7 +14,7 @@ export const CONFIG_VERSION = "0.1.0";
14
14
  * entrypoint) so core modules — telemetry, signup provenance — can read it
15
15
  * without an import cycle. `VERSION` in `cli/index.ts` re-exports this.
16
16
  */
17
- export const CLI_VERSION = "1.7.0"; // x-release-please-version
17
+ export const CLI_VERSION = "1.8.0"; // x-release-please-version
18
18
  /** The User-Agent on every request to a Zenrows API. The gateway reads the version from it. */
19
19
  export const CLI_USER_AGENT = `zenrows-cli/${CLI_VERSION}`;
20
20
  /** Env var to override the Fetch and Extract API base (local/staging testing). */
@@ -13,6 +13,8 @@ export class ToolkitError extends Error {
13
13
  suggested_commands;
14
14
  server_code;
15
15
  crawl_id;
16
+ job_id;
17
+ idempotency_key;
16
18
  retry_after;
17
19
  constructor(shape) {
18
20
  super(shape.message);
@@ -23,6 +25,8 @@ export class ToolkitError extends Error {
23
25
  this.suggested_commands = shape.suggested_commands ?? [];
24
26
  this.server_code = shape.server_code;
25
27
  this.crawl_id = shape.crawl_id;
28
+ this.job_id = shape.job_id;
29
+ this.idempotency_key = shape.idempotency_key;
26
30
  this.retry_after = shape.retry_after;
27
31
  }
28
32
  toJSON() {
@@ -34,6 +38,8 @@ export class ToolkitError extends Error {
34
38
  suggested_commands: this.suggested_commands,
35
39
  ...(this.server_code ? { server_code: this.server_code } : {}),
36
40
  ...(this.crawl_id ? { crawl_id: this.crawl_id } : {}),
41
+ ...(this.job_id ? { job_id: this.job_id } : {}),
42
+ ...(this.idempotency_key ? { idempotency_key: this.idempotency_key } : {}),
37
43
  ...(this.retry_after !== undefined ? { retry_after: this.retry_after } : {}),
38
44
  };
39
45
  }
@@ -1,9 +1,16 @@
1
1
  import { ToolkitError } from "./errors.js";
2
2
  import { findWorkspace, readJson, workspacePaths, writeJson } from "./workspace.js";
3
+ /**
4
+ * Safe defaults. The per-run caps let a default run submit up to 10,000 URLs
5
+ * in any mode: an Adaptive Stealth Mode task is estimated at its 25-credit
6
+ * upper bound, so 10,000 of them estimate at 250,000 credits. Larger runs
7
+ * (the Batch API takes up to 100,000 tasks per job) need the caps raised
8
+ * explicitly with `zenrows policy set`.
9
+ */
3
10
  export function defaultPolicy() {
4
11
  return {
5
- max_credits_per_run: 10000,
6
- max_pages_per_run: 1000,
12
+ max_credits_per_run: 250_000,
13
+ max_pages_per_run: 10_000,
7
14
  max_concurrency: 20,
8
15
  allow_browser: true,
9
16
  allow_experimental: false,
@@ -94,8 +101,8 @@ export function assertWithinLimits(requested, policy, context = "run") {
94
101
  code: "POLICY_LIMIT_EXCEEDED",
95
102
  message: `This ${context} submits ${requested.pages} page(s), over the policy cap of ${policy.max_pages_per_run}.`,
96
103
  likely_cause: "policy.max_pages_per_run bounds how many URLs a single run may submit.",
97
- next_action: `Split the job into runs of ≤${policy.max_pages_per_run} page(s), or raise max_pages_per_run in policy.json.`,
98
- suggested_commands: ["zenrows policy show"],
104
+ next_action: `Split the job into runs of at most ${policy.max_pages_per_run} page(s), or raise the cap: \`zenrows policy set max_pages_per_run ${requested.pages}\` (default ${defaultPolicy().max_pages_per_run}).`,
105
+ suggested_commands: [`zenrows policy set max_pages_per_run ${requested.pages}`, "zenrows policy show"],
99
106
  });
100
107
  }
101
108
  if (requested.credits !== undefined && requested.credits > policy.max_credits_per_run) {
@@ -103,8 +110,8 @@ export function assertWithinLimits(requested, policy, context = "run") {
103
110
  code: "POLICY_LIMIT_EXCEEDED",
104
111
  message: `This ${context} is estimated at ${requested.credits} credit(s), over the policy cap of ${policy.max_credits_per_run}.`,
105
112
  likely_cause: "policy.max_credits_per_run bounds the estimated credit cost of a single run.",
106
- next_action: "Reduce the job size or per-task cost (e.g. drop premium_proxy/js_render), or raise max_credits_per_run in policy.json.",
107
- suggested_commands: ["zenrows policy show"],
113
+ next_action: `Reduce the job size or per-task cost (e.g. drop premium_proxy/js_render), or raise the cap: \`zenrows policy set max_credits_per_run ${requested.credits}\` (default ${defaultPolicy().max_credits_per_run}). The estimate is an upper bound: an Adaptive Stealth Mode task counts 25 credits, and is billed 1 to 25.`,
114
+ suggested_commands: [`zenrows policy set max_credits_per_run ${requested.credits}`, "zenrows policy show"],
108
115
  });
109
116
  }
110
117
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenrows/cli",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "Open-source CLI, MCP, skills, templates, workflows, recipes, and evals layer for giving AI agents reliable access to protected web data through Zenrows cloud infrastructure.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -23,11 +23,14 @@ zenrows batch estimate jobs.jsonl
23
23
  ```
24
24
 
25
25
  Swap `jobs.jsonl` for your own spec. Per-line keys (`js_render`,
26
- `premium_proxy`, `proxy_country`, `mode`, `autoparse`, `external_id`) tune each
27
- URL; cost multipliers are basic 1x / JS 5x / proxy 10x / both 25x.
26
+ `premium_proxy`, `proxy_country`, `mode`, `autoparse`, `external_id`,
27
+ `metadata`, `method`, `body`) tune each URL; cost multipliers are basic 1x / JS
28
+ 5x / proxy 10x / both 25x, and Adaptive Stealth Mode costs 1x to 25x. Pass
29
+ `create`'s job-level flags (`--premium-proxy`, `--js-render`, `--manual`) to
30
+ `estimate` to price the same request.
28
31
 
29
32
  Once the spec is clean, submit it:
30
33
  ```
31
- zenrows batch create jobs.jsonl --wait
34
+ zenrows batch create jobs.jsonl --follow
32
35
  ```
33
36
  See [[batch-jobs]] for the full cloud flow.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: batch-jobs
3
- description: Scale protected fetch/extract over many URLs via Batch. Cloud create/status/results/cancel/wait/retry-failed work with an API key; estimate/validate run locally with no key.
3
+ description: "Scale protected fetch/extract over many URLs via Batch. Cloud create/list/status/wait/results/content/cancel/pause/resume/rerun/delete work with an API key; estimate/validate run locally with no key."
4
4
  version: 0.1.0
5
5
  requires_backend_capabilities: [batch]
6
6
  ---
@@ -8,22 +8,41 @@ requires_backend_capabilities: [batch]
8
8
  # Batch Jobs
9
9
 
10
10
  Process large workloads reliably and asynchronously. Batch is where Zenrows'
11
- high-scale anti-bot advantage becomes obvious — Zenrows wins when the workflow
12
- runs over thousands, millions, or recurring sets of URLs.
11
+ high-scale anti-bot advantage becomes obvious: thousands to 100,000 URLs per
12
+ job, at Fetch's prices.
13
13
 
14
14
  > Zenrows **Batch** runs on a separate host (`async.api.zenrows.com/v1`) and
15
- > spends the same credits as Fetch. The cloud subcommands work with any active
16
- > API key; a 403 maps to `BATCH_ACCESS_DENIED`. The toolkit never fakes a
17
- > cloud run.
15
+ > costs the same credits as Fetch (1 basic, 5 js_render, 10 premium_proxy, 25
16
+ > both; Adaptive Stealth Mode 1 to 25). Failed tasks are not billed, except
17
+ > target 404/410 answers, as on Fetch. The cloud subcommands work with any
18
+ > active API key; a 403 maps to `BATCH_ACCESS_DENIED`. The toolkit never fakes
19
+ > a cloud run.
20
+
21
+ ## Spec and estimate (local, no key)
18
22
 
19
- ## Local (no key, always works)
20
23
  ```
21
- zenrows batch estimate jobs.jsonl # validate the spec + estimate credits
24
+ zenrows batch estimate jobs.jsonl [--premium-proxy] [--js-render] [--manual]
22
25
  ```
23
26
 
24
- `jobs.jsonl` is one JSON object per line, each with a `url` (plus optional
25
- per-task keys: `external_id`, `metadata`, and scrape params like `js_render`,
26
- `premium_proxy`, `proxy_country`, `mode`, `autoparse`, `extract`).
27
+ `jobs.jsonl` is one JSON object per line, each with a `url` (http or https,
28
+ up to 2048 bytes). Optional per-task keys:
29
+
30
+ - `external_id`: up to 128 letters, digits, `.`, `_` or `-`; echoed on each
31
+ result row (it does not have to be unique).
32
+ - `metadata`: up to 20 string-to-string entries; keys 1 to 64 of the same
33
+ characters, values up to 1024 bytes (UTF-8), 4 KiB in total.
34
+ - `method` and `body`: `"method": "POST"` sends `body` (up to 16 KiB): a JSON
35
+ string goes as a form body, any other JSON value as JSON (`"body": null` means
36
+ no body). A POST task cannot
37
+ use `js_render`, `js_instructions` or `json_response`.
38
+ - scrape params, flat (`js_render`, `premium_proxy`, `proxy_country`, `mode`,
39
+ `autoparse`, `extract`, ...) or nested under `zenrows_params`. Booleans are
40
+ `true`/`false` (or `"true"`/`"false"`).
41
+
42
+ `estimate` checks every line against those limits and prices the run the way
43
+ `create` would submit it, job-level flags included, as a range: Adaptive
44
+ Stealth Mode tasks count 1 at the low end and 25 at the upper bound. It exits 1
45
+ on any invalid line.
27
46
 
28
47
  ## Extract in Batch
29
48
 
@@ -39,42 +58,140 @@ correctly. The result carries two keys: `html` (the
39
58
  raw page) and `parsed` (the structured data). Validate on a single URL with
40
59
  `zenrows extract <url>` before running the full batch.
41
60
 
42
- ## Cloud (needs a key)
61
+ ## Create, wait, read
62
+
43
63
  ```
44
- zenrows batch create jobs.jsonl [--proxy-country cc] [--wait]
45
- zenrows batch create jobs.jsonl --manual [--js-render] [--premium-proxy]
46
- zenrows batch status <id> # run status + stats
47
- zenrows batch results <id> [--status successful|failed|all] [--out results.jsonl]
48
- zenrows batch wait <id> [--timeout ms]
49
- zenrows batch cancel <id> # stop an in-flight run
50
- zenrows batch retry-failed <id> # rerun only the failed tasks (new run)
64
+ zenrows batch create jobs.jsonl --follow --json
65
+ zenrows batch create jobs.jsonl --manual --premium-proxy --proxy-country us --output markdown
66
+ zenrows batch create jobs.jsonl --name nightly --external-id run-42 --metadata team=growth
67
+ zenrows batch results <id> --out results.jsonl # every row, as JSONL
68
+ zenrows batch results <id> --download out/ # each result body + _manifest.jsonl
69
+ zenrows batch content <id> <task_id> # one task's body
51
70
  ```
52
71
 
53
- Every task runs in Adaptive Stealth Mode (`mode=auto`) by default, like `fetch`
54
- and `extract`: Zenrows enables JS rendering and premium proxies only on the pages
55
- that need them, and bills only the configuration that succeeds. A task that sets
56
- `js_render`, `premium_proxy` or `mode` itself keeps its own settings, and
57
- `--js-render` / `--premium-proxy` force that configuration on every task instead.
58
- `--manual` turns Adaptive Stealth Mode off. `batch estimate` counts auto tasks at
59
- the 25x upper bound, so it's the most the run can cost, not the expected cost.
60
-
61
- `create` validates the spec first, then submits. Outside Adaptive Stealth Mode,
62
- `--proxy-country` needs `--premium-proxy` and is rejected before any request.
63
- Job-level flags apply to every task; per-task keys in the JSONL override them.
72
+ - Every GET task runs in Adaptive Stealth Mode (`mode=auto`) by default, like
73
+ `fetch` and `extract`: Zenrows enables JS rendering and premium proxies only
74
+ on the pages that need them, and bills only the configuration that
75
+ succeeds. A task that turns on `js_render` or `premium_proxy`, or sets
76
+ `mode`, itself keeps its own settings; `--js-render` / `--premium-proxy` force that
77
+ configuration on every task; `--manual` turns Adaptive Stealth Mode off.
78
+ Outside Adaptive Stealth Mode, `--proxy-country` needs `--premium-proxy`.
79
+ - Job-level flags apply to every task; per-task keys override them.
80
+ - `--follow` (alias `--wait`) waits until the run ends, the run is paused, or
81
+ `--timeout` runs out (default 10 minutes; `30s`, `5m`, `1h`; a bare number
82
+ is milliseconds). A wait that runs out exits 0 and prints the run with a
83
+ `note` and `next: zenrows batch wait <id>`; the job keeps running. Ctrl-C
84
+ stops the wait, not the job, and exits 130.
85
+ - A submit of 10,000 tasks or more answers 202 with `ingest_status: pending`
86
+ while the API still receives the tasks; results stay partial until `done`.
87
+ - `create` and `rerun` send an `Idempotency-Key` (generated, or
88
+ `--idempotency-key`) and retry network errors and 502/503/504 under it.
89
+ When the outcome stays unknown, the error carries `idempotency_key` and the
90
+ exact command that repeats the request safely (the API keeps a key 24 hours
91
+ and answers a repeat with the original job). Never repeat with a new key.
92
+ - Results are kept 14 days by default; after that a row reads `expired`, has no
93
+ `result_url`, and `content` answers `BATCH_RESULT_EXPIRED`. `--download`
94
+ skips expired rows and counts them. A row's `result_url` is valid 2 hours:
95
+ list the results again for a fresh one.
96
+ - `results --status successful|failed|processing|all`, `--run <run_id>` for an
97
+ earlier run. On a run still in progress the output has `partial: true`.
98
+
99
+ With `--json`, `create`, `status`, `wait`, `cancel`, `pause`, `resume` and
100
+ `rerun` print `{ok, job_id, run_id, status, stats, job}` (plus `jobId`, kept
101
+ for older scripts), with `paused`, `ingest_status`, `accepted_tasks`,
102
+ `rerun_of`, `retried_tasks`, `inherited_tasks`, `failure_reason` and
103
+ `failure_detail` when the API sends them, and `note` when a wait ends early.
104
+
105
+ ## While it runs, and after
64
106
 
65
- A run ends in one of `completed`, `failed`, `stopped`, or `deleted`. `status`,
66
- `wait`, and `create --wait` exit non-zero (`ok: false` under `--json`) only for
67
- `failed`, and print the run's `failure_reason` and `failure_detail`. When
68
- `failure_reason` is `api_key_cap_reached`, the key hit its own credit cap
69
- (`KEY_CREDIT_CAP_REACHED`): the account still has credits, other keys keep
70
- working, and the tasks not yet run stay pending. Wait for the cap to reset
71
- (`zenrows usage`) or raise the cap; do not resubmit the job with the same key.
72
-
73
- Deferred (documented, not yet in the CLI): CSV upload, open/queue jobs,
74
- scheduled jobs, webhooks/HMAC, and ZIP export.
107
+ ```
108
+ zenrows batch list [--status open|closed] [--type regular|scheduled]
109
+ zenrows batch status <id>
110
+ zenrows batch wait <id> --timeout 30m
111
+ zenrows batch pause <id> # no new task is picked up
112
+ zenrows batch resume <id>
113
+ zenrows batch cancel <id> # stop the run; an ended run answers as it ended
114
+ zenrows batch retry-failed <id> # rerun the failed tasks
115
+ zenrows batch retry-failed <id> --include-pending # also the unfinished ones
116
+ zenrows batch rerun <id> # failed + pending tasks; successful ones carry over
117
+ zenrows batch rerun <id> --status failed,pending,processing
118
+ zenrows batch rerun <id> --all # every task (not with --status)
119
+ zenrows batch delete <id> --yes # job, runs and stored results; frees its slot
120
+ ```
75
121
 
76
- Validate locally and fan out with `zenrows fetch` per URL on a small sample
77
- before running a full batch.
122
+ A run ends `completed`, `stopped`, `failed` or `deleted`. A paused run is not
123
+ ended: `status` shows `paused: true`, and `wait` returns at once with a note
124
+ to `zenrows batch resume <id>`.
125
+
126
+ `status`, `wait`, `create --follow` and `rerun --follow` exit 1 (`ok: false`)
127
+ only for `failed`. A run fails when an account problem stops it, and the
128
+ error carries the same code and advice as the matching refused request, with
129
+ the run's `failure_reason` as `server_code` and the `job_id`:
130
+
131
+ - `insufficient_credits` → `POLICY_MAX_CREDITS_EXCEEDED`: add credits or claim
132
+ the account (`zenrows usage`).
133
+ - `subscription_inactive` → `AUTH_INVALID`: fix the subscription in Billing.
134
+ - `api_key_cap_reached` → `KEY_CREDIT_CAP_REACHED`: the account still has
135
+ credits and other keys keep working; wait for the cap to reset or raise it.
136
+ - anything else → `BATCH_FAILED`.
137
+
138
+ After any of these, and after a `cancel`, the tasks not yet run stay `pending`
139
+ (some can stay `processing`): fix the cause, then run them with
140
+ `zenrows batch rerun <id> --status failed,pending,processing`. Do not resubmit
141
+ the job. `rerun` with no flag reruns `failed,pending`, never the successful
142
+ tasks. A partial rerun (the default, or `--status`) works for 10 days after the
143
+ run started; after that only a full rerun (`zenrows batch rerun <id> --all`)
144
+ does. Every rerun is
145
+ a new run that answers 202 with `ingest_status: pending`.
146
+
147
+ ## Limits
148
+
149
+ - 100,000 tasks per job; 3 active jobs per account, shared with Crawl. A
150
+ closed job frees its slot when its run ends; an open or scheduled job only
151
+ when deleted.
152
+ - The CLI's local policy caps a run at 10,000 URLs and 250,000 estimated
153
+ credits by default (an Adaptive Stealth Mode task counts 25), so a default
154
+ run of up to 10,000 URLs passes in any mode. For more, raise both:
155
+ `zenrows policy set max_pages_per_run 100000` and
156
+ `zenrows policy set max_credits_per_run 2500000`. A workspace created by an
157
+ earlier CLI version keeps the caps in its `policy.json` (1,000 and 10,000);
158
+ `POLICY_LIMIT_EXCEEDED` prints the exact command to raise them.
159
+ - Results 14 days; `result_url` 2 hours; partial reruns 10 days; a task that
160
+ fails transiently is retried automatically up to 3 times.
161
+
162
+ Schedules, webhooks, ZIP exports, CSV inputs, open jobs (add tasks, close),
163
+ the runs list and task history are available through the Batch API and the
164
+ SDKs (Python, Node.js, Go), not this CLI.
165
+
166
+ ## Errors
167
+
168
+ Every Batch error carries the API's code as `server_code` (when it sent one)
169
+ and `job_id` on any call about one job. Reads retry 429/502/503/504 and network
170
+ errors up to 3 times; `cancel`, `pause` and `resume` retry 502/503/504; `create`
171
+ and `rerun` retry the same under their Idempotency-Key. The "Retry?" column is
172
+ for errors that remain after these retries.
173
+
174
+ | Code | Meaning | Retry? |
175
+ | --- | --- | --- |
176
+ | `BATCH_ACCESS_DENIED` | 403: Batch is not enabled for this account or key | no |
177
+ | `AUTH_INVALID` | 401: the key was rejected; or the subscription is not active (401 `subscription_inactive`, 402 "Subscription has expired."; fix it in Billing) | no |
178
+ | `POLICY_MAX_CREDITS_EXCEEDED` | 402 `payment_required`: the account is out of credits | after credits renew or are added |
179
+ | `KEY_CREDIT_CAP_REACHED` | 402 `api_key_cap_reached`: this key reached its credit cap | after the cap resets or is raised |
180
+ | `BATCH_QUOTA_EXCEEDED` | 429 `quota_exceeded`: 3 active jobs (shared with Crawl), 5 scheduled jobs, or an open job's 100,000 tasks | after a slot frees (`zenrows batch list`) |
181
+ | `BATCH_NOT_FOUND` | 404: wrong id, deleted, or another account's job | no |
182
+ | `BATCH_RUN_IN_PROGRESS` | 409 `run_not_terminal`: the run is still going (or still receiving tasks) | after `wait` or `cancel` |
183
+ | `BATCH_RUN_ENDED` | 409 `run_not_stoppable`: the run already ended (pause/resume) | no; `rerun` starts a new run |
184
+ | `BATCH_NO_MATCHING_TASKS` | 409 `no_matching_tasks`: no task has the statuses asked for | with `--status failed,pending,processing`, or `rerun --all` |
185
+ | `BATCH_RERUN_WINDOW_EXPIRED` | 409 `partial_rerun_window_expired`: a partial rerun is over 10 days late | with `rerun --all` |
186
+ | `BATCH_REQUEST_IN_FLIGHT` | 409 `idempotency_key_conflict`: the key is in use or was used for another body | same command, same key, a moment later |
187
+ | `BATCH_CONFLICT` | any other 409 (job state, key no longer valid, concurrent change) | read the cause |
188
+ | `BATCH_TASK_NOT_READY` | 409 `task_not_ready` on `content`: the task has not finished | after `wait` |
189
+ | `BATCH_TASK_FAILED` | 422 on `content`: the task failed | after `retry-failed` |
190
+ | `BATCH_RESULT_EXPIRED` | 410 `result_expired`: past the 14-day visibility window | after `rerun --all` |
191
+ | `BATCH_FAILED` | the run ended `failed` for another reason; a 400 (fix the flagged tasks) or 5xx | 5xx: yes |
192
+ | `POLICY_LIMIT_EXCEEDED` | the local policy caps (see Limits) | after raising the cap |
193
+ | `BACKEND_UNAVAILABLE` | the API could not be reached | yes |
78
194
 
79
195
  Rules: **never batch before validating** the workflow on a single page
80
- ([[extract]]). Mind the cost multipliers ([[cost-control]]).
196
+ ([[extract]]). Mind the cost multipliers ([[cost-control]]). Read errors with
197
+ [[trace-debug]].
@@ -34,7 +34,7 @@ zenrows crawl results <id> --out urls.jsonl # same, as JSONL
34
34
  a depth has no `depth` field in its output.
35
35
  - `--max-items` (kept URLs) and `--max-pages` (fetched pages) cap the crawl;
36
36
  both default to 100. `--max-pages` bounds the cost: each page is one fetch.
37
- The local policy `max_pages_per_run` caps `--max-pages` (default 1000);
37
+ The local policy `max_pages_per_run` caps `--max-pages` (default 10000);
38
38
  above it the CLI refuses with `POLICY_LIMIT_EXCEEDED` before any request.
39
39
  - `--include-patterns` / `--exclude-patterns` are substring matches on the
40
40
  normalized URL and repeat:
@@ -43,8 +43,23 @@ zenrows trace export <run-id> # JSON for sharing
43
43
  local policy credit limit was hit). Do not retry-loop; `zenrows usage` shows
44
44
  when credits renew. Top up or upgrade, or claim the account if it is an
45
45
  auto-created Free plan.
46
- - `BATCH_FAILED` → the batch run ended `failed`; `failure_reason` and
47
- `failure_detail` in `zenrows batch status <id> --json` say why.
46
+ - `BATCH_FAILED` → the batch run ended `failed` for a reason other than
47
+ credits, subscription or key cap (those get `POLICY_MAX_CREDITS_EXCEEDED`,
48
+ `AUTH_INVALID` or `KEY_CREDIT_CAP_REACHED`, with the run's `failure_reason`
49
+ as `server_code` and its `job_id`); `failure_detail` in
50
+ `zenrows batch status <id> --json` says why. Once fixed, run the unfinished
51
+ tasks with `zenrows batch rerun <id> --status failed,pending,processing`;
52
+ do not resubmit. Also a 400 (fix the flagged tasks) or a 5xx (retry).
53
+ - `BATCH_QUOTA_EXCEEDED` → 429: 3 active jobs (shared with Crawl), 5 scheduled
54
+ jobs, or an open job's task cap. Nothing was created; free a slot (see
55
+ `zenrows batch list` and `zenrows crawl list`).
56
+ - `BATCH_ACCESS_DENIED` / `BATCH_NOT_FOUND` → 403 Batch not enabled, or 404
57
+ wrong or deleted job id. Do not retry.
58
+ - `BATCH_RUN_IN_PROGRESS`, `BATCH_RUN_ENDED`, `BATCH_NO_MATCHING_TASKS`,
59
+ `BATCH_RERUN_WINDOW_EXPIRED`, `BATCH_REQUEST_IN_FLIGHT`, `BATCH_CONFLICT`,
60
+ `BATCH_TASK_NOT_READY`, `BATCH_TASK_FAILED`, `BATCH_RESULT_EXPIRED` → the
61
+ Batch API's 409/410/422 answers; `server_code` names the case and
62
+ `next_action` the fix. See [[batch-jobs]].
48
63
  - `CRAWL_NOT_ENABLED` → the API answered 403 `REQS008`: Crawl is not
49
64
  enabled for this account. Do not retry; ask Zenrows support for access.
50
65
  - `CRAWL_INSUFFICIENT_CREDITS` / `CRAWL_KEY_CAP_REACHED` → Crawl's 402: the
@@ -43,7 +43,8 @@ If the user has a start page (listing, index, section) but not the item URLs:
43
43
  If the user has many URLs (bulk):
44
44
  → Use Batch. (validate one page with fetch/extract first,
45
45
  then `zenrows batch create <jobs.jsonl>`)
46
- Batch is the cheap path at scale — do NOT
46
+ Batch is the reliable path at scale, at
47
+ Fetch's prices. Do NOT
47
48
  fan out fetch/extract per URL for bulk work.
48
49
 
49
50
  If the user needs login, clicks, forms, sessions, or persistent state:
@@ -81,7 +82,7 @@ Run `zenrows status` for the live capability matrix. As of this toolkit:
81
82
  | MCP | `zenrows mcp` | **available**: `https://mcp.zenrows.com/mcp` |
82
83
 
83
84
  - Extract: `extract=auto` falls back to Autoparse on a domain not prepared yet.
84
- - Batch: the cheapest path at scale (create, status, results). Estimate specs locally with no key.
85
+ - Batch: the reliable path at scale, at Fetch's prices (create, list, status, wait, results, content, pause, resume, rerun, delete). Estimate specs locally with no key.
85
86
  - Crawl: collects the URLs behind one start page, optionally with each page's HTML.
86
87
  - Browser: escalation-only, for pages Fetch can't handle; same backend as the MCP `browser_*` tools.
87
88
 
@@ -3,8 +3,9 @@
3
3
  Scaffold for a high-scale workload expressed as a JSONL job spec, run on the
4
4
  Zenrows **Batch**. One JSON object per line, each with a `url` (plus
5
5
  optional per-line overrides like `js_render`, `premium_proxy`, `proxy_country`,
6
- `mode`, `autoparse`, `extract`, and an `external_id` echoed back on each
7
- result).
6
+ `mode`, `autoparse`, `extract`, an `external_id` echoed back on each result,
7
+ string `metadata`, and `"method": "POST"` with a `body`). `batch estimate`
8
+ checks every line against the API's limits.
8
9
 
9
10
  Set `"extract": "auto"` on a line to run that URL through Extract — structured
10
11
  data instead of raw HTML, at the same credit cost as a regular task. Extract
@@ -18,17 +19,21 @@ zenrows batch estimate jobs.example.jsonl # validate the spec + estimate cred
18
19
 
19
20
  ## Cloud (needs a key)
20
21
 
21
- The cloud subcommands run with any active API key and spend the same credits
22
+ The cloud subcommands run with any active API key and cost the same credits
22
23
  as Fetch.
23
24
 
24
25
  ```bash
25
- zenrows batch create jobs.example.jsonl --wait # submit + wait for the run to finish
26
+ zenrows batch create jobs.example.jsonl --follow # submit + wait for the run to finish
26
27
  zenrows batch status <job-id> # run status + progress stats
27
28
  zenrows batch results <job-id> --out results.jsonl # collect results (paginated)
28
- zenrows batch retry-failed <job-id> # rerun only the failed tasks
29
+ zenrows batch retry-failed <job-id> --include-pending # rerun what did not succeed
29
30
  zenrows batch cancel <job-id> # stop an in-flight run
30
31
  ```
31
32
 
33
+ The local policy caps a run at 10,000 URLs and 250,000 estimated credits by
34
+ default; raise them with `zenrows policy set max_pages_per_run <n>` and
35
+ `zenrows policy set max_credits_per_run <n>`.
36
+
32
37
  Tasks use Adaptive Stealth Mode (`mode=auto`) by default. Job-level scrape
33
38
  options come from flags (`--js-render`, `--premium-proxy`, `--proxy-country`,
34
39
  `--output`, or `--manual` to turn Adaptive Stealth Mode off); per-line keys