> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zenrows.com/llms.txt
> Use this file to discover all available pages before exploring further.

# FAQ (Frequently Asked Questions)

> Frequently asked questions about the Zenrows Batch: access, pricing, limits, scraping options, results, scheduling, and how it relates to Fetch.

Quick answers to the questions teams ask most when they start with Batch. For step-by-step code, see the [developer guide](/batch/developer-guide-restapi). For error-specific help, see [Troubleshooting](/batch/troubleshooting).

## Getting Started

<AccordionGroup>
  <Accordion title="Is Batch available to everyone?">
    Yes. Batch is in **Beta**, available to every Zenrows account. You can start submitting jobs right away with your existing API key, with no extra setup.
  </Accordion>

  <Accordion title="Do I need a separate API key?">
    No. Batch uses the **same API key as your Fetch**. There's nothing extra to generate.
  </Accordion>

  <Accordion title="What's the base URL?">
    All REST requests go to `https://async.api.zenrows.com/v1`. Send your key in the `X-API-Key` header on every request.
  </Accordion>

  <Accordion title="How do I integrate?">
    Batch is a REST API, so you can call it from any language with an HTTP client. The [developer guide](/batch/developer-guide-restapi) shows a dependency-free pattern using Node's `fetch` that translates directly to Python, Go, PHP, Java, and more.
  </Accordion>
</AccordionGroup>

## How It Works

<AccordionGroup>
  <Accordion title="What's the difference between a job, a run, and a task?">
    A **job** is the template: your URLs and configuration. A **run** is one execution of that job, where each rerun or scheduled fire creates a new run. A **task** is a single URL within a run, with its own status and result.
  </Accordion>

  <Accordion title="What's the difference between a rerun and retrying failures?">
    A **rerun** replays every task in the job as a fresh run. **Retry failed** re-executes only the tasks that failed (and optionally those that never started), carrying over the successful results so you only pay to re-scrape what failed. Both create a new run on the same job.
  </Accordion>

  <Accordion title="How do I know when a job is done?">
    Two ways: attach a **webhook** and the API sends a `run.completed` event when the run finishes, or **poll** the job and watch the `stats` counters (`completed === successful + failed`). Webhooks are a convenience; the run state remains the source of truth.
  </Accordion>

  <Accordion title="Can I pause or stop a running job?">
    Yes. `POST /jobs/{id}/pause` reversibly suspends the current run and `POST /jobs/{id}/resume` continues it, while `POST /jobs/{id}/stop` (alias `POST /jobs/{id}/cancel`) ends it for good. These act on the job's current run, which is different from pausing a scheduled job's recurring schedule. See [Control the current run](/batch/developer-guide-restapi#control-the-current-run).
  </Accordion>

  <Accordion title="My submit returned 202 instead of 201. Is that an error?">
    No. `POST /jobs` returns `201 Created` for submissions under 10,000 tasks and `202 Accepted` for larger ones (10,000+ tasks or a CSV input). The response body is identical and you track completion the same way, so treat both the same. For a large `202`, `GET /jobs/{id}/results` may return partial pages for a few seconds while the task list finishes writing, but `stats.total` is correct from the start.
  </Accordion>

  <Accordion title="Can I submit URLs as I go, instead of all at once?">
    Yes. Open a job, add tasks in batches over time (up to 10,000 per add-tasks call), then close it with `last_batch: true`. See [open jobs](/batch/developer-guide-restapi#open-jobs-adding-tasks-in-batches). Note that open (queue-mode) jobs are still being finalized during the beta.
  </Accordion>

  <Accordion title="How many jobs can I run at the same time?">
    During the beta, an account can have **3 active jobs concurrently**. Submitting a 4th returns `429 quota_exceeded`. Wait for a job to finish before submitting the next, or contact Zenrows if you need a higher limit.
  </Accordion>
</AccordionGroup>

## Scraping Options

<AccordionGroup>
  <Accordion title="What scraping options can I use?">
    The same `zenrows_params` as Fetch, including `js_render`, `premium_proxy`, `proxy_country`, `wait_for`, `wait`, `css_extractor`, `autoparse`, `json_response`, `custom_headers`, and more. Set them once on the job, and override any of them per URL.
  </Accordion>

  <Accordion title="Can I use different options for different URLs in the same job?">
    Yes. Options set on the job apply to every URL; options set on an individual URL override the job-level ones for that URL. This lets you configure the common case once and tune the exceptions (for example, a different `proxy_country` for a handful of URLs).
  </Accordion>

  <Accordion title="Can one job include URLs from different websites?">
    Yes. A job is just a list of URLs plus the options to apply to them, so you can mix targets in a single job and use per-URL overrides where a specific site needs different handling.
  </Accordion>

  <Accordion title="What output formats are available?">
    Raw HTML, parsed **JSON**, **Markdown**, **plain text**, and **PDF**. The API derives the output format from your options (for example `response_type: markdown`) and records it on each result.
  </Accordion>
</AccordionGroup>

## Results and Limits

<AccordionGroup>
  <Accordion title="How long are result download links valid?">
    Each result's `result_url` is a presigned link valid for **2 hours**. If it expires, re-list the results to get a fresh one rather than storing it. You can also stream a task's content directly, or export an entire run as a ZIP.
  </Accordion>

  <Accordion title="How many URLs can a job handle?">
    Up to **100,000 URLs per job submit**. On an open job, each add-tasks call accepts up to **10,000 URLs**, and CSV uploads support up to **50 MB / 100,000 rows**. Results page at \~1,000 per page.
  </Accordion>

  <Accordion title="How do I match results back to my own records?">
    Attach an `external_id` to each URL. The API returns it unchanged on every result, so you can correlate results to your records without tracking the API's task IDs. It doesn't need to be unique.
  </Accordion>

  <Accordion title="Can I download all results at once?">
    Yes. Start a bulk export to bundle a run's result bodies into a single ZIP. Exports expire 12 hours after creation, and a run whose combined results exceed 1 GiB can't be exported as one archive. Page through the results instead.
  </Accordion>
</AccordionGroup>

## Scheduling

<AccordionGroup>
  <Accordion title="Can I run a job on a recurring schedule?">
    Yes. Schedule a job as **one-shot** (a single future time), **interval** (every N minutes, hours, or days), or **calendar** (specific times on specific days). Schedules are timezone-aware using IANA names. You can pause and resume any time without losing the schedule.
  </Accordion>

  <Accordion title="Why was my scheduled time rejected?">
    Times of day must be **full hours**: use `"09:00"`, not `"09:30"`. Also provide exactly one schedule type (`at`, `rate`, or `calendar`) and a valid IANA timezone for `at` and `calendar`.
  </Accordion>
</AccordionGroup>

## Pricing

<AccordionGroup>
  <Accordion title="How is Batch priced?">
    Pricing is **identical to Fetch**: you're billed in **credits, per successful request**. The credit cost depends on the scraping tier each URL uses.

    | Configuration                        | Credits per successful request |
    | ------------------------------------ | ------------------------------ |
    | Base (no special options)            | 1                              |
    | JavaScript rendering                 | 5                              |
    | Premium proxy                        | 10                             |
    | JavaScript rendering + premium proxy | 25                             |
    | Adaptive Stealth Mode (`mode=auto`)  | Dynamic, 1–25                  |
  </Accordion>

  <Accordion title="Am I charged for failed requests?">
    No. **Only successful requests cost credits.** A run with failures costs less than its up-front estimate.
  </Accordion>

  <Accordion title="Can I estimate the cost before running a job?">
    Yes. Because the rate card is small and stable, you can estimate credits client-side with no API call, then show your users an accurate range up front. After a run finishes, you can also read the actual credits it consumed. See [Estimate cost](/batch/developer-guide-restapi#estimate-cost) and [Check actual cost](/batch/developer-guide-restapi#check-actual-cost).
  </Accordion>

  <Accordion title="How can I keep costs as low as possible?">
    Use **Adaptive Stealth Mode** (`mode=auto`). It starts each request with the cheapest viable setup and escalates to JavaScript rendering or premium proxies only when a site actually needs them, so you pay for the lightest configuration that succeeds.
  </Accordion>
</AccordionGroup>

## Reliability and Security

<AccordionGroup>
  <Accordion title="What happens if a page is temporarily unavailable?">
    The API retries transient failures automatically, so a page that's briefly unavailable still has a strong chance of being fetched. Requests are also paced to stay polite to the sites you scrape.
  </Accordion>

  <Accordion title="What does the idempotency key do, and is it safe to retry a submit?">
    An **idempotency key** guards against accidentally creating duplicate jobs. Each key can be used once: the first submit with a given key creates the job, and any later submit reusing that key returns `409 idempotency_key_conflict`, whether or not the body is identical. The API does not replay the original response. Because of this, an idempotency key is **not** a safe-retry token. If a submit fails midway, don't resend with the same key. First check whether the job was created (list your recent jobs), then reuse its `job_id` or submit again with a new key.
  </Accordion>

  <Accordion title="How do I verify that a webhook really came from Zenrows?">
    Set `signature: true` when you attach the webhook. The API then HMAC-signs each delivery, and your receiver verifies the `X-Signature` header against the raw request body using your organization's active key. See [Manage HMAC keys](/batch/developer-guide-restapi#manage-hmac-keys).
  </Accordion>
</AccordionGroup>

## Didn't Find Your Answer?

Check the [Troubleshooting](/batch/troubleshooting) page for error-specific guidance, or reach out to support with your `job_id`, the request body (API key redacted), and the exact error `code` and `detail`.
