```yaml product: AlterLab title: Batch Scraping category: guides comparison_context: "AlterLab is an alternative to Firecrawl, ScrapingBee, and Bright Data." source_url: https://alterlab.io/docs/guides/batch-scraping ``` # Batch Scraping Submit up to 100 URLs in a single API call. Jobs are processed in parallel and you can poll for results or receive a webhook when the batch completes. ## How It Works 1. **Submit** -- POST an array of URLs to `/api/v1/batch`. Credits are pre-debited for the estimated total cost 2. **Process** -- Each URL becomes an individual job processed in parallel with full tier escalation 3. **Collect** -- Poll `GET /api/v1/batch/{batch_id}` for results, or receive a `batch.completed` webhook ## Submit a Batch ``` POST /api/v1/batch ``` ```bash curl -X POST https://api.alterlab.io/api/v1/batch \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "urls": [ {"url": "https://example.com/page-1"}, {"url": "https://example.com/page-2", "mode": "js"}, {"url": "https://example.com/page-3", "formats": ["text", "markdown"]} ], "webhook_url": "https://your-server.com/webhook" }' ``` ### Per-URL Options Each item in `urls` accepts the same parameters as a single scrape request: | Field | Default | Description | | ------------------- | ------------------ | -------------------------------------------- | | `url` | -- | Target URL (required) | | `mode` | `"auto"` | auto, html, js, pdf, ocr | | `formats` | `["html", "json"]` | Output formats: text, json, html, markdown | | `cost_controls` | null | force_tier, max_tier, prefer_cost, fail_fast | | `extraction_schema` | null | JSON schema for structured extraction | | `cache` | false | Enable caching for this URL | | `timeout` | 90 | Per-URL timeout in seconds (1-300) | | `wait_for` | null | CSS selector to wait for | ### Response (202 Accepted) ```json { "batch_id": "batch_7a3b9c8d-...", "total_urls": 3, "status": "processing", "estimated_credits": 45000, "job_ids": ["a1b2c3d4-...", "e5f6g7h8-...", "i9j0k1l2-..."] } ``` ## Poll Batch Status ``` GET /api/v1/batch/{batch_id} ``` ```json { "batch_id": "batch_7a3b9c8d-...", "status": "completed", "total": 3, "completed": 2, "failed": 1, "items": [ { "job_id": "...", "url": "...", "status": "succeeded", "result": { "text": "..." } }, { "job_id": "...", "url": "...", "status": "failed", "error": "BLOCKED_BY_ANTIBOT" } ] } ``` | Status | Meaning | | ------------ | --------------------------- | | `processing` | Some jobs are still running | | `completed` | All jobs succeeded | | `partial` | All jobs done, some failed | | `failed` | All jobs failed | ## Webhook Delivery If you provide a `webhook_url`, AlterLab sends a `batch.completed` POST when every job finishes: ```json { "event": "batch.completed", "timestamp": "2026-03-03T12:01:00Z", "data": { "batch_id": "batch_7a3b9c8d-...", "status": "partial", "total": 3, "completed": 2, "failed": 1 } } ``` ### Signature Verification for Batch Webhooks The `batch.completed` webhook is a **lifecycle webhook** — it signs the raw request body: ``` HMAC-SHA256(secret, raw_body) ``` See the [Webhooks guide](/docs/guides/webhooks#signature-verification) for the full verification example. If you are using stream connector webhooks (`output_config.webhook_stream`), see [Stream Connector Webhooks](/docs/guides/webhooks#stream-connector-webhooks-crawlpage_completed) — they use a different timestamp-prefixed signing format. ## Billing - Credits are **pre-debited** when the batch is submitted - Failed jobs are automatically **refunded** - BYOP discounts apply per-URL ## Limits - Maximum **100 URLs** per batch request - Each URL counts as a separate scrape credit-wise - Jobs are processed in parallel (no guaranteed order) - Batch metadata expires after **24 hours**