> ## 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.

# Setting Up Batch

> Get started with the Zenrows Batch: find your API key, authenticate, submit your first batch job, and retrieve the results.

This guide walks you through setting up the Zenrows Batch. You'll confirm your access, locate your API key, authenticate, submit your first batch job, and download the results end to end.

<Note>
  Batch is in **Beta**, available to every Zenrows account. You just need an active Zenrows API key; your existing Fetch key works without any extra setup.
</Note>

## Initial Setup

Follow these steps to get ready:

<Steps>
  <Step title="Sign in to Zenrows">
    Visit the [Registration Page](https://app.zenrows.com/register) to create an account, or log in if you already have one. Batch uses the **same API key as Fetch**, which you'll find in your dashboard.
  </Step>

  <Step title="Note the base URL">
    All REST requests go to `https://async.api.zenrows.com/v1`.
  </Step>
</Steps>

## Authentication

Authenticate every request with your API key in the `X-API-Key` header. For security, the key travels in a header and never in the URL.

```bash theme={null}
X-API-Key: YOUR_ZENROWS_API_KEY
```

Store the key in an environment variable so your code can read it instead of hard-coding it:

```bash theme={null}
export ZENROWS_API_KEY="YOUR_ZENROWS_API_KEY"
```

## Submit Your First Job

This minimal example submits a **closed job** (one where you know all URLs up front) with JavaScript rendering enabled. Replace `YOUR_ZENROWS_API_KEY` with your key.

<CodeGroup>
  ```python Python theme={null}
  ## pip install requests
  import requests, json

  headers = {'X-API-Key': 'YOUR_ZENROWS_API_KEY', 'Content-Type': 'application/json'}
  payload = {
      'type': 'regular',
      'status': 'closed',
      'zenrows_params': {'js_render': 'true'},
      'tasks': [
          {'url': 'https://www.scrapingcourse.com/ecommerce/product/abominable-hoodie'},
          {'url': 'https://www.scrapingcourse.com/ecommerce/product/adrienne-trek-jacket'},
      ],
  }
  response = requests.post('https://async.api.zenrows.com/v1/jobs', headers=headers, data=json.dumps(payload))
  job = response.json()
  print('submitted', job['job_id'], job['latest_run']['status'])
  ```

  ```javascript Node.js theme={null}
  // npm install axios
  const axios = require('axios');

  axios({
      url: 'https://async.api.zenrows.com/v1/jobs',
      method: 'POST',
      headers: { 'X-API-Key': 'YOUR_ZENROWS_API_KEY' },
      data: {
          type: 'regular',
          status: 'closed',
          zenrows_params: { js_render: 'true' },
          tasks: [
              { url: 'https://www.scrapingcourse.com/ecommerce/product/abominable-hoodie' },
              { url: 'https://www.scrapingcourse.com/ecommerce/product/adrienne-trek-jacket' },
          ],
      },
  })
      .then(response => console.log('submitted', response.data.job_id, response.data.latest_run.status))
      .catch(error => console.log(error));
  ```

  ```java Java theme={null}
  import org.apache.hc.client5.http.fluent.Request;
  import org.apache.hc.core5.http.ContentType;

  public class FirstJob {
      public static void main(final String... args) throws Exception {
          String body = "{"
              + "\"type\":\"regular\",\"status\":\"closed\","
              + "\"zenrows_params\":{\"js_render\":\"true\"},"
              + "\"tasks\":["
              + "{\"url\":\"https://www.scrapingcourse.com/ecommerce/product/abominable-hoodie\"},"
              + "{\"url\":\"https://www.scrapingcourse.com/ecommerce/product/adrienne-trek-jacket\"}"
              + "]}";
          String response = Request.post("https://async.api.zenrows.com/v1/jobs")
                  .addHeader("X-API-Key", "YOUR_ZENROWS_API_KEY")
                  .bodyString(body, ContentType.APPLICATION_JSON)
                  .execute().returnContent().asString();
          System.out.println(response);
      }
  }
  ```

  ```php PHP theme={null}
  <?php
  $payload = json_encode([
      'type' => 'regular',
      'status' => 'closed',
      'zenrows_params' => ['js_render' => 'true'],
      'tasks' => [
          ['url' => 'https://www.scrapingcourse.com/ecommerce/product/abominable-hoodie'],
          ['url' => 'https://www.scrapingcourse.com/ecommerce/product/adrienne-trek-jacket'],
      ],
  ]);
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, 'https://async.api.zenrows.com/v1/jobs');
  curl_setopt($ch, CURLOPT_POST, 1);
  curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
  curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: YOUR_ZENROWS_API_KEY', 'Content-Type: application/json']);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  $response = curl_exec($ch);
  echo $response . PHP_EOL;
  curl_close($ch);
  ?>
  ```

  ```go Go theme={null}
  package main

  import (
      "bytes"
      "io"
      "log"
      "net/http"
  )

  func main() {
      payload := []byte(`{
          "type": "regular",
          "status": "closed",
          "zenrows_params": { "js_render": "true" },
          "tasks": [
              { "url": "https://www.scrapingcourse.com/ecommerce/product/abominable-hoodie" },
              { "url": "https://www.scrapingcourse.com/ecommerce/product/adrienne-trek-jacket" }
          ]
      }`)
      req, err := http.NewRequest("POST", "https://async.api.zenrows.com/v1/jobs", bytes.NewBuffer(payload))
      if err != nil {
          log.Fatalln(err)
      }
      req.Header.Set("X-API-Key", "YOUR_ZENROWS_API_KEY")
      req.Header.Set("Content-Type", "application/json")
      resp, err := http.DefaultClient.Do(req)
      if err != nil {
          log.Fatalln(err)
      }
      defer resp.Body.Close()
      body, _ := io.ReadAll(resp.Body)
      log.Println(string(body))
  }
  ```

  ```ruby Ruby theme={null}
  # gem install faraday
  require 'faraday'
  require 'json'

  payload = {
      type: 'regular',
      status: 'closed',
      zenrows_params: { js_render: 'true' },
      tasks: [
          { url: 'https://www.scrapingcourse.com/ecommerce/product/abominable-hoodie' },
          { url: 'https://www.scrapingcourse.com/ecommerce/product/adrienne-trek-jacket' }
      ]
  }
  conn = Faraday.new
  res = conn.post('https://async.api.zenrows.com/v1/jobs') do |req|
      req.headers['X-API-Key'] = 'YOUR_ZENROWS_API_KEY'
      req.headers['Content-Type'] = 'application/json'
      req.body = payload.to_json
  end
  print(res.body)
  ```

  ```bash cURL theme={null}
  curl -X POST "https://async.api.zenrows.com/v1/jobs" \
    -H "X-API-Key: YOUR_ZENROWS_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "type": "regular",
      "status": "closed",
      "zenrows_params": { "js_render": "true" },
      "tasks": [
        { "url": "https://www.scrapingcourse.com/ecommerce/product/abominable-hoodie" },
        { "url": "https://www.scrapingcourse.com/ecommerce/product/adrienne-trek-jacket" }
      ]
    }'
  ```
</CodeGroup>

The response includes a `job_id` and the initial run status. Poll `GET /jobs/{id}` until the run reaches a **terminal** state (finished and no longer changing), then collect the results.

### Get Your Results

Once the run is complete, list the successful tasks and download each one's scraped content. Every result row carries a `result_url`, a presigned link to the scraped page that is valid for 2 hours. Replace `JOB_ID` with the `job_id` from the submit response.

<CodeGroup>
  ```python Python theme={null}
  ## pip install requests
  import requests

  headers = {'X-API-Key': 'YOUR_ZENROWS_API_KEY'}
  page = requests.get('https://async.api.zenrows.com/v1/jobs/JOB_ID/results',
                      headers=headers, params={'status': 'successful'}).json()
  for row in page['results']:
      html = requests.get(row['result_url']).text  # the scraped page
      print(row.get('external_id') or row['task_id'], len(html), 'chars')
  ```

  ```javascript Node.js theme={null}
  // npm install axios
  const axios = require('axios');

  const page = (await axios.get('https://async.api.zenrows.com/v1/jobs/JOB_ID/results', {
      headers: { 'X-API-Key': 'YOUR_ZENROWS_API_KEY' },
      params: { status: 'successful' },
  })).data;
  for (const row of page.results) {
      const html = (await axios.get(row.result_url)).data; // the scraped page
      console.log(row.external_id ?? row.task_id, String(html).length, 'chars');
  }
  ```

  ```java Java theme={null}
  import com.fasterxml.jackson.databind.JsonNode;
  import com.fasterxml.jackson.databind.ObjectMapper;
  import org.apache.hc.client5.http.fluent.Request;

  public class GetResults {
      public static void main(final String... args) throws Exception {
          ObjectMapper mapper = new ObjectMapper();
          JsonNode page = mapper.readTree(
                  Request.get("https://async.api.zenrows.com/v1/jobs/JOB_ID/results?status=successful")
                          .addHeader("X-API-Key", "YOUR_ZENROWS_API_KEY")
                          .execute().returnContent().asString());
          for (JsonNode row : page.path("results")) {
              String html = Request.get(row.path("result_url").asText())
                      .execute().returnContent().asString(); // the scraped page
              String id = row.path("external_id").asText(row.path("task_id").asText());
              System.out.println(id + " " + html.length() + " chars");
          }
      }
  }
  ```

  ```php PHP theme={null}
  <?php
  $ch = curl_init('https://async.api.zenrows.com/v1/jobs/JOB_ID/results?status=successful');
  curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: YOUR_ZENROWS_API_KEY']);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  $page = json_decode(curl_exec($ch), true);
  curl_close($ch);
  foreach ($page['results'] as $row) {
      $html = file_get_contents($row['result_url']); // the scraped page
      echo ($row['external_id'] ?? $row['task_id']) . ' ' . strlen($html) . ' chars' . PHP_EOL;
  }
  ?>
  ```

  ```go Go theme={null}
  package main

  import (
      "encoding/json"
      "io"
      "log"
      "net/http"
  )

  func main() {
      req, _ := http.NewRequest("GET", "https://async.api.zenrows.com/v1/jobs/JOB_ID/results?status=successful", nil)
      req.Header.Set("X-API-Key", "YOUR_ZENROWS_API_KEY")
      resp, err := http.DefaultClient.Do(req)
      if err != nil {
          log.Fatalln(err)
      }
      defer resp.Body.Close()
      body, _ := io.ReadAll(resp.Body)
      var page struct {
          Results []struct {
              TaskID     string `json:"task_id"`
              ExternalID string `json:"external_id"`
              ResultURL  string `json:"result_url"`
          } `json:"results"`
      }
      json.Unmarshal(body, &page)
      for _, row := range page.Results {
          r, _ := http.Get(row.ResultURL) // the scraped page
          html, _ := io.ReadAll(r.Body)
          r.Body.Close()
          name := row.ExternalID
          if name == "" {
              name = row.TaskID
          }
          log.Println(name, len(html), "chars")
      }
  }
  ```

  ```ruby Ruby theme={null}
  # gem install faraday
  require 'faraday'
  require 'json'

  page = JSON.parse(Faraday.get('https://async.api.zenrows.com/v1/jobs/JOB_ID/results?status=successful') { |req|
      req.headers['X-API-Key'] = 'YOUR_ZENROWS_API_KEY'
  }.body)
  page['results'].each do |row|
      html = Faraday.get(row['result_url']).body # the scraped page
      puts "#{row['external_id'] || row['task_id']} #{html.length} chars"
  end
  ```

  ```bash cURL theme={null}
  # 1) List the successful results; each row has a presigned result_url valid 2h.
  curl "https://async.api.zenrows.com/v1/jobs/JOB_ID/results?status=successful" \
    -H "X-API-Key: YOUR_ZENROWS_API_KEY"

  # 2) Download one result's scraped HTML using its result_url from the response above.
  curl "RESULT_URL"
  ```
</CodeGroup>

<Tip>
  Want to see the cost before you run anything? You can estimate credits client-side with no API call. See [Estimate cost](/batch/developer-guide-restapi#6-cost).
</Tip>

## Next Steps

<CardGroup cols={2}>
  <Card title="Developer Guide: REST API" icon="code" href="/batch/developer-guide-restapi">
    Every feature against the REST API: open jobs, CSV upload, scheduling, webhooks, and more.
  </Card>

  <Card title="Troubleshooting" icon="wrench" href="/batch/troubleshooting">
    Diagnose authentication, submission, download, and webhook issues fast.
  </Card>

  <Card title="FAQ" icon="circle-question" href="/batch/faq">
    Quick answers on access, pricing, limits, and scraping options.
  </Card>
</CardGroup>
