> ## 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 Scraper API: access, pricing, limits, scraping options, results, scheduling, and how it relates to the Universal Scraper API.

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

## Getting Started

<AccordionGroup>
  <Accordion title="Is the Batch Scraper API generally available?">
    Not yet. The Batch Scraper API is in **private beta**, so access is granted per account. Once your account is invited, you can start submitting jobs right away with your existing API key.
  </Accordion>

  <Accordion title="Do I need a separate API key?">
    No. The Batch Scraper API uses the **same API key as your Universal Scraper API**. 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?">
    The Batch Scraper API is a REST API, so you can call it from any language with an HTTP client. The [developer guide](/batch-scraper-api/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="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-scraper-api/developer-guide-restapi#open-jobs-adding-tasks-in-batches). Note that open (queue-mode) jobs are still being finalized during the private beta.
  </Accordion>

  <Accordion title="How many jobs can I run at the same time?">
    During the private 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 the Universal Scraper API, 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 the Batch Scraper API priced?">
    Pricing is **identical to the Universal Scraper API**: you're billed in **usage, per successful request**. The usage cost depends on the scraping tier each URL uses.

    | Configuration                        | Usage 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 usage.** 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 usage 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 usage it consumed. See [Estimate cost](/batch-scraper-api/developer-guide-restapi#estimate-cost) and [Check actual cost](/batch-scraper-api/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-scraper-api/developer-guide-restapi#manage-hmac-keys).
  </Accordion>
</AccordionGroup>

## Didn't Find Your Answer?

Check the [Troubleshooting](/batch-scraper-api/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`.
