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

# POST /batch/{endpoint}

> Run brand, enrich, company, classify, screenshot or styleguide on up to 500 URLs as one job, with a webhook when it's done. Free to submit; each URL costs what one call costs.

## Overview

`/batch/{endpoint}` runs one endpoint over a list of URLs or bare domains — up to 500 — as a single job:

| Path | Each URL is answered as |
| - | - |
| `POST /batch/enrich` | [`GET /enrich`](/api-reference/endpoint/enrich) |
| `POST /batch/brand` | [`GET /brand`](/api-reference/endpoint/brand) |
| `POST /batch/company` | [`GET /company`](/api-reference/endpoint/company) |
| `POST /batch/classify` | [`GET /classify`](/api-reference/endpoint/classify) |
| `POST /batch/screenshot` | [`GET /screenshot`](/api-reference/endpoint/screenshot) |
| `POST /batch/styleguide` | [`GET /styleguide`](/api-reference/endpoint/styleguide) |

You get a job id straight away. Each URL is answered exactly as a single call would answer it — curated data first, then a live extraction — and results are read from [`GET /batch/{endpoint}/{id}`](/api-reference/endpoint/batch-status), or delivered to your webhook as each URL finishes. One request instead of hundreds, and no concurrency limit to manage on your side.

To scrape a list of pages into markdown, use [`POST /batch/scrape`](/api-reference/endpoint/batch-scrape).

## Example requests

<CodeGroup>
  ```bash Enrich a list of domains theme={"theme":"css-variables"}
  curl -X POST "https://api.prefetch.io/batch/enrich" \
    -H "X-API-Key: $PREFETCH_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "urls": ["stripe.com", "linear.app", "notion.so"],
      "webhook": "https://example.com/hooks/prefetch"
    }'
  ```

  ```bash Brand kits with design tokens theme={"theme":"css-variables"}
  curl -X POST "https://api.prefetch.io/batch/brand" \
    -H "X-API-Key: $PREFETCH_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "urls": ["https://vercel.com", "https://figma.com"],
      "options": { "design_tokens": true },
      "concurrency": 3
    }'
  ```

  ```javascript Node.js — submit and poll theme={"theme":"css-variables"}
  const submit = await fetch("https://api.prefetch.io/batch/enrich", {
    method: "POST",
    headers: { "X-API-Key": process.env.PREFETCH_API_KEY, "Content-Type": "application/json" },
    body: JSON.stringify({ urls: ["stripe.com", "linear.app", "notion.so"] }),
  });
  const { data: job } = await submit.json();

  let status;
  do {
    await new Promise((r) => setTimeout(r, 5000));
    const res = await fetch(`https://api.prefetch.io/batch/enrich/${job.id}`, {
      headers: { "X-API-Key": process.env.PREFETCH_API_KEY },
    });
    status = (await res.json()).data;
  } while (status.status === "pending" || status.status === "running");

  for (const item of status.data) {
    console.log(item.url, item.ok ? item.company?.name : item.error_code);
  }
  ```
</CodeGroup>

## Example response

```json theme={"theme":"css-variables"}
{
  "success": true,
  "data": {
    "id": "crw_7c2e9a4b1d0f4e6a8b3c5d7e9f1a2b3c",
    "status": "pending",
    "endpoint": "enrich",
    "url_count": 3,
    "invalid_urls": [],
    "created_at": "2026-10-13T09:14:03.221Z",
    "expires_at": "2026-10-14T09:14:03.221Z"
  },
  "meta": { "requestId": "1d2c3b4a-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "durationMs": 212 }
}
```

## Options

`options` are the endpoint's own parameters, as `GET /{endpoint}` takes them, applied to every URL — snake\_case or camelCase, typed JSON or strings:

| Endpoint | Example `options` |
| - | - |
| brand, enrich | `{ "design_tokens": true, "css": true, "screenshot_colors": 5 }` |
| screenshot | `{ "device": "iphone_15", "dark_mode": true, "format": "webp" }` |
| styleguide | `{ "dark_mode": true }` |
| company, classify | none |

They are checked before anything is queued: an invalid option is a `400` naming it.

Also: `concurrency` (URLs in parallel, 1–5, default 2), `ignore_invalid_urls` (drop malformed or blocklisted URLs, listed in `invalid_urls`, instead of rejecting the batch), and `webhook`. Duplicates — including the same site under another spelling — run once.

## Webhooks

`webhook` works as it does for [crawls](/api-reference/endpoint/crawl#webhooks), with events named for the endpoint: `batch_enrich.started`, `batch_enrich.page` (one per URL, carrying its result), `batch_enrich.completed` and `batch_enrich.failed`. Each delivery is signed with the secret returned when the batch is created.

Webhooks are not available through RapidAPI: poll the status endpoint instead.

## Credits

Submitting is free. Each URL answered costs **what one call of that endpoint costs** — 12 for enrich, 10 for brand or styleguide, 5 for company or classify, 1 for screenshot — charged as URLs complete: when you poll the status endpoint, or when the batch ends, whichever comes first, and never twice. URLs that fail are never charged.

A batch your key cannot pay for is refused up front with `CREDITS_EXHAUSTED`, and the message says how many URLs fit: a 500-domain `/batch/enrich` can cost up to 6,000 credits.

## Related

<CardGroup cols={2}>
  <Card title="GET /batch/{endpoint}/{id}" icon="list-check" href="/api-reference/endpoint/batch-status">
    Read progress and results.
  </Card>

  <Card title="POST /batch/scrape" icon="file-lines" href="/api-reference/endpoint/batch-scrape">
    A list of pages as markdown.
  </Card>
</CardGroup>


## OpenAPI

````yaml POST /batch/{endpoint}
openapi: 3.1.0
info:
  title: Prefetch API
  description: >-
    Extract brand identity, company data, screenshots, IAB classifications, and
    clean page content from any URL.
  version: 1.0.0
  contact:
    email: support@prefetch.io
servers:
  - url: https://api.prefetch.io
    description: Production
security:
  - apiKey: []
tags:
  - name: Endpoints
    description: Data extraction endpoints. All require authentication and consume credits.
  - name: Health
    description: Health and readiness probes. No authentication required.
paths:
  /batch/{endpoint}:
    post:
      tags:
        - Endpoints
      summary: Run an endpoint on many URLs
      description: >-
        Runs `brand`, `enrich`, `company`, `classify`, `screenshot` or
        `styleguide` on up to 500 URLs or bare domains as one job, and returns
        its id immediately. Each URL is answered exactly as `GET /{endpoint}`
        would answer it — curated data first, then a live extraction — with
        `options` (that endpoint's own parameters) applied to every URL. Poll
        `GET /batch/{endpoint}/{id}` for progress and results, or pass a
        `webhook`: events are named `batch_{endpoint}.*` (`batch_enrich.page`
        per URL, `batch_enrich.completed` at the end).


        **Credit cost: nothing to submit, then the endpoint's own price per URL
        answered** (enrich 12, brand 10, styleguide 10, classify 5, company 5,
        screenshot 1). URLs that fail are never charged. A key whose balance
        cannot cover every URL is refused up front with `CREDITS_EXHAUSTED`.
        Webhooks are not available through RapidAPI.
      operationId: createBatch
      parameters:
        - schema:
            type: string
            enum:
              - brand
              - enrich
              - company
              - classify
              - screenshot
              - styleguide
            description: What every URL is run through. /batch/scrape has its own page.
            example: enrich
          required: true
          description: What every URL is run through. /batch/scrape has its own page.
          name: endpoint
          in: path
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BatchBody'
      responses:
        '202':
          description: Batch queued.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: '#/components/schemas/BatchJobData'
                  meta:
                    $ref: '#/components/schemas/Meta'
                required:
                  - success
                  - data
                  - meta
              example:
                success: true
                data:
                  id: crw_7c2e9a4b1d0f4e6a8b3c5d7e9f1a2b3c
                  status: pending
                  endpoint: enrich
                  url_count: 3
                  invalid_urls: []
                  created_at: '2026-10-13T09:14:03.221Z'
                  expires_at: '2026-10-14T09:14:03.221Z'
                meta:
                  requestId: 6f1d2c3b-4a5e-4f60-8b7a-9c0d1e2f3a4b
                  durationMs: 1840
        '400':
          description: Invalid or missing parameters.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Revoked key, expired key, blocked URL, or credit limit.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate or concurrency limit exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: Service temporarily unavailable — retry shortly.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    BatchBody:
      type: object
      properties:
        urls:
          type: array
          items:
            type: string
          description: >-
            The URLs or bare domains to run (1–500). Duplicates run once.
            `domains` is accepted too.
          example:
            - stripe.com
            - https://linear.app
            - notion.so
        options:
          type: object
          additionalProperties:
            nullable: true
          description: >-
            The endpoint's own parameters, as GET /{endpoint} takes them,
            applied to every URL — e.g. `{"design_tokens": true}` for brand or
            enrich, `{"device": "iphone_15"}` for screenshot. Typed JSON or
            strings.
          example:
            design_tokens: true
        concurrency:
          type: integer
          description: 'URLs run in parallel (1–5). Default: 2.'
        ignore_invalid_urls:
          type: boolean
          description: >-
            Drop URLs that fail validation (listed in `invalid_urls`) instead of
            rejecting the whole batch. Default: false.
        webhook:
          $ref: '#/components/schemas/CrawlWebhook'
      required:
        - urls
    BatchJobData:
      type: object
      properties:
        id:
          type: string
          example: crw_4b1e9c0a7d2f4e6b
        endpoint:
          type: string
          description: What each URL is run through.
          example: scrape
        status:
          type: string
          enum:
            - pending
            - running
            - completed
            - failed
            - cancelled
        url_count:
          type: integer
          description: >-
            Pages queued, after removing duplicates (and invalid URLs, with
            ignore_invalid_urls).
        invalid_urls:
          type: array
          items:
            type: string
          description: URLs dropped by ignore_invalid_urls.
        created_at:
          type: string
          format: date-time
        expires_at:
          type: string
          format: date-time
        webhook:
          type: object
          description: Present when the request set `webhook`.
          properties:
            url:
              type: string
            events:
              type: array
              items:
                type: string
            secret:
              type: string
              description: >-
                Verifies the `X-Prefetch-Signature` on each delivery. Shown only
                here — store it.
      required:
        - id
        - status
        - url_count
        - invalid_urls
        - created_at
        - expires_at
    Meta:
      type: object
      properties:
        requestId:
          type: string
          format: uuid
          description: Unique identifier for this request.
          example: a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d
        durationMs:
          type: integer
          nullable: true
          description: Time taken to process the request in milliseconds.
          example: 1842
      required:
        - requestId
        - durationMs
    ErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          enum:
            - false
          description: >-
            Always false. Absent, like `meta`, on authentication and rate-limit
            errors, which are answered before a request is processed.
        error:
          type: string
          description: >-
            Human-readable error message. The wording can change: branch on
            `error_code`.
          example: Credit limit exceeded
        error_code:
          type: string
          enum:
            - INVALID_REQUEST
            - PAYLOAD_TOO_LARGE
            - NOT_FOUND
            - CONFLICT
            - UNAUTHORIZED
            - FORBIDDEN
            - CREDITS_EXHAUSTED
            - RATE_LIMITED
            - CONCURRENCY_LIMITED
            - URL_BLOCKED
            - URL_NOT_PERMITTED
            - DNS_RESOLUTION_FAILED
            - TARGET_BLOCKED
            - TARGET_CONNECTION_FAILED
            - TARGET_TIMEOUT
            - TARGET_SSL_ERROR
            - TARGET_SERVER_ERROR
            - TARGET_CLIENT_ERROR
            - TARGET_NOT_A_PAGE
            - TARGET_UNLOADABLE
            - RESEARCH_INCONCLUSIVE
            - TIMEOUT
            - SERVICE_BUSY
            - SERVICE_UNAVAILABLE
            - INTERNAL_ERROR
          description: >-
            What went wrong, for machines. Stable: a code keeps its meaning once
            published. See [Error handling](/concepts/errors).
          example: CREDITS_EXHAUSTED
        retryable:
          type: boolean
          description: >-
            Whether the same request can succeed if sent again after a short
            wait (honour `Retry-After` when present). False for problems with
            the request or the target.
          example: false
        retryAfterSeconds:
          type: integer
          description: 'On `RATE_LIMITED`: seconds until a request will be accepted.'
        limit:
          type: integer
          description: >-
            On `CONCURRENCY_LIMITED`: how many requests this key may have in
            flight.
        meta:
          $ref: '#/components/schemas/Meta'
      required:
        - error
        - error_code
        - retryable
    CrawlWebhook:
      anyOf:
        - type: string
          description: An https:// URL; every event is sent.
        - type: object
          properties:
            url:
              type: string
              example: https://example.com/hooks/prefetch
            events:
              type: array
              items:
                type: string
                enum:
                  - started
                  - page
                  - completed
                  - failed
              description: 'Which events to send. Default: all four.'
            headers:
              type: object
              additionalProperties:
                type: string
              description: Sent with every delivery (up to 20).
            metadata:
              type: object
              additionalProperties:
                nullable: true
              description: Echoed back in every payload (up to 4 KB).
          required:
            - url
      description: >-
        Where to send progress. Each delivery is signed — `X-Prefetch-Signature:
        t=<unix>,v1=<hex>`, HMAC-SHA256 of `<t>.<raw body>` with the `secret`
        returned when the job is created — the same scheme as monitor webhooks.
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: X-API-Key
      description: Your Prefetch API key. Obtain one from the dashboard.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.