```yaml product: AlterLab title: REST API Reference category: api comparison_context: "AlterLab is an alternative to Firecrawl, ScrapingBee, and Bright Data." source_url: https://alterlab.io/docs/api/rest ``` # REST API Reference Complete reference for the AlterLab REST API. All endpoints use JSON for requests and responses. > OpenAPI publication: the API route and schema tests cover this contract dynamically. The > checked-in `web/public/openapi.json`, version file, and versioned archive are intentionally > left unchanged here; the `staging` `sync-openapi` workflow regenerates and publishes the > next version together with its immutable archive. **Base URL:** `https://api.alterlab.io/api/v1` ## Authentication All scraping endpoints require an API key passed via the `X-API-Key` header: ``` X-API-Key: sk_live_your_key_here ``` ## Unified Scrape Endpoint ### POST /api/v1/scrape Scrape a single URL with automatic tier escalation. **Request Body:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `url` | string | Yes | Target URL to scrape | | `mode` | string | No | `auto` (default), `html`, `js`, `pdf`, `ocr` | | `formats` | string[] | No | Output formats: `html`, `json`, `text`, `markdown` | | `extraction_schema` | object | No | JSON Schema for structured data extraction | | `extraction_profile` | string | No | Pre-built profile: `product`, `article`, `job_posting` | | `extraction_prompt` | string | No | Natural language extraction prompt | | `wait_for` | string | No | CSS selector to wait for before extraction | | `wait_until` | string | No | Navigation event: `load`, `domcontentloaded`, or `networkidle` | | `enable_scroll` | boolean | No | Scroll page for lazy-loaded content. `true`: always scroll, `false`: never scroll, omit: auto | | `cache` | boolean | No | Enable response caching (default: false) | | `cache_ttl` | integer | No | Cache TTL in seconds | | `timeout` | integer | No | Request timeout in seconds (1-300) | | `sync` | boolean | No | Block until completion when possible (default: true). Set `false` for an immediate job handoff. | | `sync_policy` | string | No | `auto` (default) returns the existing early `202` for predicted higher-tier jobs; `wait_until_deadline` keeps a synchronous request open until terminal completion or the safe API deadline. Requires `sync: true`. | | `webhook_url` | string | No | URL for async result delivery | | `location` | object | No | Geo-targeting options (country, city) | | `advanced` | object | No | Advanced browser options (see below) | | `cost_controls` | object | No | Cost control settings (see below) | **Advanced Options (`advanced`):** | Option | Type | Description | |--------|------|-------------| | `render_js` | boolean or `"auto"` | Render JavaScript with headless browser (forces Tier 4). `"auto"` detects JS-heavy pages automatically | | `screenshot` | boolean | Capture full-page screenshot (+$0.0002, requires `render_js=true`) | | `generate_pdf` | boolean | Generate PDF of rendered page (+$0.0004, requires `render_js=true`) | | `markdown` | boolean | Extract content as Markdown (free) | | `ocr` | boolean | Extract text from images using OCR (+$0.001) | | `use_proxy` | boolean | Route request through premium proxy (+$0.0002) | | `scroll_to_load` | boolean | Scroll page to trigger lazy-loaded content (requires `render_js`) | | `session_id` | string | UUID of a stored authenticated session | | `cookies` | object | Inline cookie key-value map for one-off use | **Cost Controls (`cost_controls`):** | Option | Type | Description | |--------|------|-------------| | `max_credits` | number | Maximum credits to spend on this request | | `force_tier` | string | Force a specific tier, skipping escalation (`1`, `2`, `3`, `3.5`, `4`) | | `max_tier` | string | Maximum tier to escalate to (`1`, `2`, `3`, `3.5`, `4`) | | `prefer_cost` | boolean | Optimize for cost — try cheaper tiers first | | `prefer_speed` | boolean | Optimize for speed — skip to a reliable tier | | `fail_fast` | boolean | Return error instead of escalating to more expensive tiers | **Example Request:** ```bash curl -X POST https://api.alterlab.io/api/v1/scrape \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com", "formats": ["json", "markdown"], "extraction_schema": { "type": "object", "properties": { "title": {"type": "string"}, "price": {"type": "number"} } } }' ``` **Wait-until-deadline example:** Use `wait_until_deadline` when a synchronous caller wants predicted higher-tier jobs (and runtime tier escalation) to remain on the connection. The safe API deadline is capped at 85 seconds (less any time spent before polling). If it is reached first, the response is the same canonical `202` job handoff shown below; the worker continues processing and the returned `poll_url` remains usable. ```bash curl -X POST https://api.alterlab.io/api/v1/scrape \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com", "sync": true, "sync_policy": "wait_until_deadline" }' ``` **Response (200 - Sync):** ```json { "url": "https://example.com", "status_code": 200, "content": "...", "title": "Example Domain", "filtered_content": { "title": "Example Product", "price": 29.99 }, "billing": { "total_credits": 1, "tier_used": "1" } } ``` **Response (202 - Async):** Every `202` response from `POST /api/v1/scrape` that includes a `job_id` also includes `poll_url`. It is always the canonical relative polling path `/api/v1/jobs/{job_id}` (derived from the returned job ID; do not build it from the request host). ```json { "job_id": "a1b2c3d4-...", "poll_url": "/api/v1/jobs/a1b2c3d4-..." } ``` ## Batch Scraping ### POST /api/v1/batch Submit up to 100 URLs for parallel async processing. ```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"} ], "webhook_url": "https://your-server.com/webhook" }' ``` Each item in `urls` accepts the same parameters as `/api/v1/scrape`. ### GET /api/v1/batch/{batch_id} Poll batch status and retrieve results. ```json { "batch_id": "batch_7a3b9c8d-...", "status": "completed", "total": 2, "completed": 2, "failed": 0, "items": [ {"job_id": "...", "url": "...", "status": "succeeded", "result": {...}}, {"job_id": "...", "url": "...", "status": "succeeded", "result": {...}} ] } ``` Batch status values: `processing`, `completed`, `partial`, `failed`. ## Cost Estimation ### POST /api/v1/scrape/estimate Estimate the cost of scraping a URL before making the request. ```bash curl -X POST https://api.alterlab.io/api/v1/scrape/estimate \ -H "X-API-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"url": "https://linkedin.com"}' ``` ## Usage & Balance ### GET /api/v1/billing/balance Check your current balance and usage. ```bash curl https://api.alterlab.io/api/v1/billing/balance \ -H "X-API-Key: YOUR_API_KEY" ``` ## Tier Escalation & Pricing AlterLab automatically escalates through tiers to successfully scrape each URL: | Tier | Name | Cost | Per $1 | Use Case | |------|------|------|--------|----------| | 1 | Curl | $0.0002 | 5,000 | Static HTML sites | | 2 | HTTP | $0.0003 | 3,333 | TLS fingerprinting | | 3 | Stealth | $0.002 | 500 | Browser checks | | 4 | Browser | $0.004 | 250 | JS-heavy SPAs | | 5 | Captcha | $0.02 | 50 | CAPTCHA solving | ## Rate Limits - 100 requests/second per API key (burst) - 10,000 requests/minute sustained - Batch: 100 URLs per request ## Error Handling | Status | Error | Description | |--------|-------|-------------| | 400 | `VALIDATION_ERROR` | Invalid request parameters | | 401 | `UNAUTHORIZED` | Missing or invalid API key | | 402 | `INSUFFICIENT_CREDITS` | Balance too low | | 404 | `NOT_FOUND` | Resource not found | | 422 | `SCRAPE_FAILED` | Scraping failed after all tier attempts | | 429 | `RATE_LIMITED` | Too many requests | | 500 | `INTERNAL_ERROR` | Server error |