# QuantumProxies.io API reference

> QuantumProxies.io API reference: 48 endpoints with parameters, request and response examples in cURL, Python, Node.js and PHP, error codes, rate limits per…

Base URL `https://api.quantumproxies.io/v1` · Auth `Authorization: Bearer qp_live_…` · HTML: https://quantumproxies.io/docs/ · OpenAPI: https://quantumproxies.io/docs/openapi.json · All endpoints: https://quantumproxies.io/docs/llms.txt · This file: https://quantumproxies.io/docs/index.md

_Generated from openapi.json v2026-10-07 on 2026-10-07; prices and limits as of 2026-10-07_

## Authentication

Every request carries an API key in the `Authorization` header:

```http
Authorization: Bearer qp_live_…
```

`Authorization: Bearer qp_live_YOUR_API_KEY`. Keys are `qd_live_`/`qp_live_` followed by 64 hex characters and are created on the dashboard API keys page. Only this header is read: there is no `x-api-key` fallback and keys are never accepted in the query string. A disabled, expired or unknown key, or a banned account, answers 401.

Keys are created on the [API keys page](https://app.quantumproxies.io/api-keys) of the dashboard; [register](https://app.quantumproxies.io/register) if you do not have an account. Keys are shown once; keep them in an environment variable, never in client-side code.

Base URL: `https://api.quantumproxies.io/v1` (same API on the dashboard host: `https://app.quantumproxies.io/api/v1`, without the short aliases).

Machine-readable formats: [openapi.json](https://quantumproxies.io/docs/openapi.json) · [openapi.yaml](https://quantumproxies.io/docs/openapi.yaml) · [index.md](https://quantumproxies.io/docs/index.md) (this reference as Markdown) · [llms.txt](https://quantumproxies.io/docs/llms.txt).

## Errors and status codes

Status codes used by this API and what each one means (collected from every endpoint below). Error bodies are JSON with `type: "error"`; read `message` for the reason.
| Status | Meaning | Returned by |
| --- | --- | --- |
| `400` | Malformed input. `message` names the parameter and the rule it broke. | 28 endpoints |
| `401` | Missing, malformed, unknown, disabled or expired API key; or the account is not active. | 47 endpoints |
| `402` | The free tier plus wallet balance cannot cover the worst-case estimate of this call, or the account's monthly spend cap is reached. Nothing was run or charged. | 16 endpoints |
| `403` | No proxy pool is available to serve the call (no house plan and no residential/datacenter/IPv6 plan on the account). | 11 endpoints |
| `404` | `presetId` does not resolve to one of your presets. | 24 endpoints |
| `422` | Input that can never resolve (documentation placeholder, undelegated TLD) — before or after the run; retrying the same input cannot help. Not billed. | `POST /scraper/collectors/{slug}/run` |
| `424` | The run failed at the source (blocked, timed out, layout changed). JSON body with `run_id` and `error`; not billed, safe to retry. | `POST /scraper/collectors/{slug}/run` |
| `429` | Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After. | 47 endpoints |
| `500` | Extraction failed on our side or timed out (70 s budget). Never billed. | 36 endpoints |
| `502` | Only with `failOnBlock: true`: every tier came back blocked. Same diagnostics as the 200 form; the GB moved are debited. | `POST /scraper/unlock`, `POST /public/proxies/generate` |
| `503` | The Data API group is switched off for non-admin keys (early-access kill switch). | 39 endpoints |

Response headers related to limits: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, `Retry-After`, `X-RateLimit-Scope`, `X-RateLimit-Plan`.

## Rate limits and tiers

Limits per tier (live values as of 2026-10-07):
| Tier | Monthly | Free usage / month | Requests / minute | Batch concurrency | Unit price discount |
| --- | --- | --- | --- | --- | --- |
| Pay as you go | $0 | $2 | 20 | 5 | 0% |
| Starter | $19/month | $15 | 300 | 10 | 10% |
| Growth | $79/month | $50 | 600 | 20 | 20% |
| Scale | $299/month | $250 | 1200 | 20 | 30% |
- Per API key. Two windows apply: the tier's requests/minute (tiers.json → rateLimitPerMin) and an hourly window of max(1000, rateLimitPerMin × 60). Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (hourly window); a 429 also carries Retry-After. Plan-level limiters (per-minute budget, browser-render concurrency) add X-RateLimit-Scope and X-RateLimit-Plan to their 429s.
- rateLimitPerMin is enforced per API key; the key also has an hourly window = max(1000, rateLimitPerMin × 60) requests.
- A pay-as-you-go account in real-balance billing mode gets balance_mode_rate_limit_per_min (pricing.json) instead of its tier cap.
- 429 responses carry X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset and Retry-After.
- Real-balance billing mode: 300 requests/minute instead of the tier cap.

## Pricing per endpoint

Every pay-as-you-go account includes $2 of free usage per month. Only successful calls are billed; a blocked page, a target `4xx`/`5xx`, a timeout or an error on our side costs nothing. Prices below are the list of 2026-10-07; `GET /scraper/billing` returns the live list applied to your key. LLM tokens used by the AI endpoints are billed at provider cost × 2.
| Endpoint | Price keys |
| --- | --- |
| `POST /scraper/extract` | `extract` $0.0002 per call + $3/GB, max $0.02 per call<br>`extract_render` $0.001 per call + $4/GB, max $0.05 per call<br>`ai_extract` $0.001 per call, max $1.50 per call |
| `POST /scraper/serp` | `serp` $0.0005 per call<br>`serp_render` $0.002 per call |
| `POST /scraper/serp/bulk` | `serp_render` $0.002 per call<br>`serp` $0.0005 per call |
| `POST /scraper/map` | `map` $0.0005 per call |
| `POST /scraper/crawl` | `crawl_page` $0.0003 per call<br>`extract_render` $0.001 per call + $4/GB, max $0.05 per call |
| `POST /scraper/batch` | `batch_url` $0.0002 per call<br>`extract_render` $0.001 per call + $4/GB, max $0.05 per call |
| `POST /scraper/unlock` | `unlock_request` $2.40/GB of transferred data |
| `POST /scraper/ai` | `ai_extract` $0.001 per call, max $1.50 per call<br>`extract` $0.0002 per call + $3/GB, max $0.02 per call<br>`serp` $0.0005 per call<br>`map` $0.0005 per call |
| `POST /scraper/places-ai` | `places_ai` $0.005 per call, max $3 per call<br>`serp_render` $0.002 per call |
| `POST /scraper/shopping-ai` | `shopping_ai` $0.005 per call, max $3 per call<br>`serp_render` $0.002 per call |
| `POST /scraper/ai-visibility` | `ai_visibility` $0.002 per call<br>`ai_visibility_citation` $0.01 per call<br>`serp` $0.0005 per call |
| `POST /scraper/seo-audit` | `seo_audit` $0.0012 per call<br>`extract` $0.0002 per call + $3/GB, max $0.02 per call |
| `POST /scraper/collectors/{slug}/run` | `collector_result` — by collector, table below |
| `POST /scraper/datasets` | `dataset_row` $0.03 per call<br>`dataset_row_refresh` $0.01 per call |
| `POST /scraper/parser/generate` | `parser_generate` $0.02 per call |
| `POST /scraper/parser/presets/{id}/heal` | `parser_generate` $0.02 per call |
**Collector results** (`POST /scraper/collectors/{slug}/run`, per delivered result):

| Collector price key | Per delivered result |
| --- | --- |
| `collector_ai_readiness` | $0.003 |
| `collector_airbnb_stays` | $0.001 |
| `collector_aliexpress_search` | $0.001 |
| `collector_amazon_product` | $0.008 |
| `collector_amazon_reviews` | $0.002 |
| `collector_amazon_search` | $0.001 |
| `collector_app_store_apps` | $0.0008 |
| `collector_app_store_reviews` | $0.0005 |
| `collector_arxiv_papers` | $0.0004 |
| `collector_asos_search` | $0.001 |
| `collector_autotrader_search` | $0.001 |
| `collector_bbb_businesses` | $0.002 |
| `collector_bestbuy_product` | $0.002 |
| `collector_booking_stays` | $0.02 |
| `collector_business_directory` | $0.001 |
| `collector_capterra_products` | $0.001 |
| `collector_carvana_cars` | $0.001 |
| `collector_certificate_transparency` | $0.0004 |
| `collector_clinical_trials` | $0.0005 |
| `collector_coingecko_coins` | $0.0002 |
| `collector_company_profile` | $0.03 |
| `collector_coursera_courses` | $0.001 |
| `collector_crates_io` | $0.0003 |
| `collector_defillama` | $0.0003 |
| `collector_dns_records` | $0.0002 |
| `collector_docker_hub` | $0.0003 |
| `collector_doordash_restaurants` | $0.001 |
| `collector_ebay_product` | $0.006 |
| `collector_ebay_search` | $0.001 |
| `collector_exchange_rates` | $0.0002 |
| `collector_flipkart_search` | $0.001 |
| `collector_g2_products` | $0.001 |
| `collector_github_repos` | $0.0005 |
| `collector_gleif_lei` | $0.0004 |
| `collector_google_autocomplete` | $0.0002 |
| `collector_google_events` | $0.002 |
| `collector_google_flights` | $0.003 |
| `collector_google_jobs` | $0.001 |
| `collector_google_lens` | $0.004 |
| `collector_google_maps_places` | $0.001 |
| `collector_google_news` | $0.0005 |
| `collector_google_play_apps` | $0.006 |
| `collector_google_shopping` | $0.001 |
| `collector_google_trends` | $0.0005 |
| `collector_hacker_news` | $0.0003 |
| `collector_healthgrades_doctors` | $0.001 |
| `collector_hotels` | $0.02 |
| `collector_idealista_search` | $0.001 |
| `collector_indeed_jobs` | $0.001 |
| `collector_instagram_profile` | $0.006 |
| `collector_itunes_search` | $0.0004 |
| `collector_keyword_ideas` | $0.0002 |
| `collector_kleinanzeigen_search` | $0.001 |
| `collector_linkedin_company` | $0.005 |
| `collector_linkedin_jobs` | $0.001 |
| `collector_linkedin_profile` | $0.005 |
| `collector_local_business_leads` | $0.01 |
| `collector_marketwatch_quote` | $0.003 |
| `collector_miodottore_doctors` | $0.001 |
| `collector_nike_products` | $0.001 |
| `collector_npm_packages` | $0.0003 |
| `collector_nvd_cve` | $0.0004 |
| `collector_openalex` | $0.0004 |
| `collector_openfda` | $0.0004 |
| `collector_openlibrary_books` | $0.0004 |
| `collector_paginegialle_profiles` | $0.002 |
| `collector_place_reviews` | $0.0005 |
| `collector_product_offers` | $0.002 |
| `collector_producthunt_posts` | $0.001 |
| `collector_pypi_packages` | $0.0003 |
| `collector_reddit_comments` | $0.002 |
| `collector_reddit_posts` | $0.0005 |
| `collector_redfin_search` | $0.0008 |
| `collector_rottentomatoes_movies` | $0.001 |
| `collector_search_images` | $0.0003 |
| `collector_search_videos` | $0.0004 |
| `collector_sec_filings` | $0.0005 |
| `collector_site_contacts` | $0.02 |
| `collector_site_documents` | $0.005 |
| `collector_stackoverflow` | $0.0003 |
| `collector_steam` | $0.0008 |
| `collector_subito_search` | $0.001 |
| `collector_target_product` | $0.0006 |
| `collector_tech_stack` | $0.005 |
| `collector_tiktok_profile` | $0.004 |
| `collector_tiktok_video` | $0.004 |
| `collector_tripadvisor_search` | $0.001 |
| `collector_trustpilot_reviews` | $0.002 |
| `collector_udemy_courses` | $0.001 |
| `collector_walmart_product` | $0.0015 |
| `collector_walmart_search` | $0.002 |
| `collector_wayback_machine` | $0.0003 |
| `collector_weather_forecast` | $0.0002 |
| `collector_web_search` | $0.0004 |
| `collector_wellfound_jobs` | $0.001 |
| `collector_whois_domain` | $0.0005 |
| `collector_wikidata` | $0.0003 |
| `collector_wikipedia_articles` | $0.0005 |
| `collector_world_bank` | $0.0002 |
| `collector_yahoo_finance` | $0.001 |
| `collector_youtube_channel` | $0.0008 |
| `collector_youtube_search` | $0.0008 |
| `collector_youtube_video` | $0.005 |
| `collector_zalando_search` | $0.001 |
| `collector_zillow_property` | $0.005 |
| `collector_zillow_search` | $0.001 |

## MCP server

The same API is available as an MCP server for Claude, Cursor, ChatGPT and any MCP client. Product page: [quantumproxies.io/mcp-server](https://quantumproxies.io/mcp-server).

**Remote server (OAuth 2.1, no key to paste):** `https://api.quantumproxies.io/mcp`

OAuth metadata: [authorization server](https://app.quantumproxies.io/.well-known/oauth-authorization-server) · [protected resource](https://api.quantumproxies.io/.well-known/oauth-protected-resource/mcp)

Claude Code (remote, OAuth):

```bash
claude mcp add --transport http quantumproxies https://api.quantumproxies.io/mcp
```

**Local server (npx, API key in the environment):**

```bash
QUANTUMPROXIES_API_KEY=qp_live_… npx -y quantumproxies-mcp
```

Claude Code (local):

```bash
claude mcp add quantumproxies -e QUANTUMPROXIES_API_KEY=qp_live_… -- npx -y quantumproxies-mcp
```

Clients without native remote MCP support can bridge through `npx -y quantumproxies-mcp-remote`.

Trial limits for keyless use are published by `GET /mcp/trial-config` (see Account).

Tools exposed by the remote server (as of 2026-10-07) and the REST endpoint behind each one:

| MCP tool | Endpoint |
| --- | --- |
| `scrape` | `POST /scraper/extract` |
| `search` | `POST /scraper/serp` |
| `search_and_read` | `POST /scraper/serp + POST /scraper/extract` |
| `search_bulk` | `POST /scraper/serp/bulk` |
| `search_bulk_status` | `GET /scraper/serp/bulk/{jobId}` |
| `map` | `POST /scraper/map` |
| `crawl` | `POST /scraper/crawl` |
| `crawl_status` | `GET /scraper/crawl/{jobId}` |
| `batch` | `POST /scraper/batch` |
| `batch_status` | `GET /scraper/batch/{jobId}` |
| `unlock` | `POST /scraper/unlock` |
| `ai_visibility` | `POST /scraper/ai-visibility` |
| `seo_audit` | `POST /scraper/seo-audit` |
| `list_collectors` | `GET /scraper/collectors` |
| `run_collector` | `POST /scraper/collectors/{slug}/run` |
| `collector_run_status` | `GET /scraper/collectors/runs/{runId}` |
| `create_dataset` | `POST /scraper/datasets` |
| `dataset_status` | `GET /scraper/datasets/{jobId}` |
| `generate_parser` | `POST /scraper/parser/generate` |
| `list_parser_presets` | `GET /scraper/parser/presets` |
| `save_parser_preset` | `POST /scraper/parser/presets` |
| `heal_parser_preset` | `POST /scraper/parser/presets/{id}/heal` |
| `parser_preset_stats` | `GET /scraper/parser/presets/{id}/stats` |
| `list_proxies` | `GET /public/proxies` |
| `generate_proxies` | `POST /public/proxies/generate` |
| `proxy_locations` | `POST /public/proxies/generate (targeting options)` |
| `whitelist_ip` | `POST /public/proxies/whitelist-ip` |
| `report` | `(MCP only: usage report of the session)` |

## Data API

Markdown for this group only: https://quantumproxies.io/docs/data-api.md

### Scrape

One URL in, Markdown/HTML/text out, with optional structured and AI extraction.

#### Scrape one URL

`POST /scraper/extract`

**Price:** `extract` $0.0002 per call + $3/GB, max $0.02 per call · `extract_render` $0.001 per call + $4/GB, max $0.05 per call · `ai_extract` $0.001 per call, max $1.50 per call. `extract` when the TLS/fetch tier served the page, `extract_render` when the browser did; both carry a perGb meter and a capUsd ceiling (pricing.json → meters). `ai_extract` + LLM tokens × ai_token_markup when ai_prompt/ai_schema is used. The pre-check reserves the worst case (render price unless engine is pinned to tls/fetch without render). _(list of 2026-10-07)_  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
**Short alias:** `POST https://api.quantumproxies.io/v1/scrape` (same handler, primary host only)  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/data-api.md)

Fetches one page through the residential pool and returns it as Markdown, HTML or text. Starts on a TLS-fingerprint tier (fast, cheap) and, with `engine: auto`, escalates to a stealth headless browser only when the target blocks; `render: true` (or `engine: render`, `screenshot`, `xhr`, `reveal_hidden`) forces the browser. Binary documents (PDF, DOCX, XLSX, CSV) are converted to text. Optional structured extraction (`extract` CSS schema or a stored `presetId`) and AI extraction (`ai_prompt`/`ai_schema`) run on the same fetch. Pass `html` instead of `url` to convert markup you already have (no fetch, no proxy bandwidth). What it does not do: it does not solve interactive captchas, and `mode: summary` returns no page content (so it cannot be combined with AI extraction). Billed per successful page by the engine that served it, plus a per-GB bandwidth component with a per-call cap; AI extraction adds the `ai_extract` fee plus marked-up LLM tokens. A page flagged as blocked or a target status ≥ 400 is not charged.

##### Request body (required)

`Content-Type: application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `url` | string | no | http/https URL to scrape. Required unless `html` is given. |
| `html` | string | no | Markup to convert instead of fetching (max 5 MB). |
| `format` | string: `markdown`, `html`, `text`, `raw` | no | `raw` is an alias of `html`. Defaults to markdown (also when AI extraction is requested). Default `"markdown"`. |
| `formats` | array of string: `markdown`, `html`, `text` | no | Extra formats returned together under `payload.formats`. |
| `data_format` | string: `markdown`, `screenshot` | no | Compatibility alias: `markdown` sets format, `screenshot` sets `screenshot: fullPage`. |
| `engine` | string: `auto`, `tls`, `fetch`, `render` | no | `auto` starts on the TLS tier and escalates to the browser on a block; `tls`/`fetch` never escalate; `render` forces the browser. Default `"auto"`. |
| `autoEscalate` | boolean | no | Allow/forbid the auto engine's escalation to the browser (ignored once the plan's render budget is spent). |
| `tlsProfile` | string | no | TLS fingerprint profile for the TLS tier (opaque to the API; see /scraper/unlock for the known names). |
| `render` | boolean | no | Force the stealth headless browser (JS execution). Bills the render rate. Default `false`. |
| `mobile` | boolean | no | Mobile viewport and user agent. Default `false`. |
| `waitMs` | number | no | Extra wait after load before capture, render tier (clamped by the service, max 15000). |
| `waitForSelector` | string | no | Wait until this CSS selector appears (render tier). |
| `scrollToBottom` | boolean | no | Auto-scroll to trigger lazy-loaded content (render tier). Default `false`. |
| `actions` | array of object (PageAction) | no |  |
| `screenshot` | boolean \\| string: `fullPage` | no | `true` = viewport PNG, `fullPage` = whole page; forces render. Returned base64 under `screenshot`. |
| `xhr` | boolean | no | Record the page's XHR/fetch traffic under `xhr` (forces render). Use it to discover which API to target with a `fetchResource` action. |
| `expect` | object | no | Assertion that the page is the real one: `element` (CSS selector, ≤512 chars) and/or `text` (≤512 chars) must be present, otherwise the fetch is treated as blocked and retried. |
| `expect.element` | string | no |  |
| `expect.text` | string | no |  |
| `extract` | object (ExtractSchema) | no | Structured-extraction schema: field name → CSS selector, or an object with `selector`, optional `attr` (attribute to read instead of text) and `all` (true = every match as an array). |
| `presetId` | string | no | Run a stored parser preset instead of an inline `extract` schema; the run is scored per field for self-healing. 404 if the preset is not yours. |
| `contentMode` | string: `smart`, `article`, `full` | no | `smart` = whole page minus nav/footer/cookie chrome; `article` = Readability main article; `full` = entire body. Alias `content_mode`. Default `"smart"`. |
| `content_modes` | array of string: `smart`, `article`, `full` | no | Return several content modes at once under `contents`. Alias `contentModes`. |
| `fullPage` | boolean | no | Legacy: `true` = contentMode full. |
| `mode` | string: `full`, `summary` | no | `summary` drops the content and returns only metadata (title, description, canonical, contentLength, engine, bytes) — the light view for audits over many pages. Default `"full"`. |
| `include_links` | boolean | no | Also return de-duplicated absolute links under `links`. Alias `includeLinks`. Default `false`. |
| `app_state` | boolean \\| string: `auto`, `raw` | no | Mine the page's own hydration state (Next/Nuxt/JSON islands) into `metadata.appState`. `true`/`auto` = pruned to the informative parts; `raw` = full blobs. Alias `appState`. |
| `parser` | object | no | Caller CSS rules: `include` (scope the content), `exclude` (drop site-specific chrome), `keep` (protect sections from smart stripping). Each an array of ≤25 selectors, ≤200 chars each. |
| `parser.include` | array of string | no |  |
| `parser.exclude` | array of string | no |  |
| `parser.keep` | array of string | no |  |
| `reveal_hidden` | boolean | no | Render tier: open `<details>`/accordions and click through tabs, capturing every panel. Alias `revealHidden`. |
| `output` | object | no | Markdown shaping; every key also accepted flat (snake_case) at the top level, flat wins. |
| `output.frontmatter` | boolean | no |  |
| `output.toc` | boolean | no |  |
| `output.linksMode` | string: `inline`, `footnote`, `strip` | no |  |
| `output.maxTokens` | number | no |  |
| `output.imagesMode` | string: `inline`, `alt`, `strip` | no |  |
| `output.query` | string | no | Relevance filter for LLM-oriented output. |
| `output.highlights` | integer | no | Most query-relevant passages to return (requires `query`). |
| `output.summarySections` | boolean | no |  |
| `output.chunk` | object | no |  |
| `frontmatter` | boolean | no |  |
| `links_mode` | string: `inline`, `footnote`, `strip` | no |  |
| `toc` | boolean | no |  |
| `max_tokens` | number | no |  |
| `images_mode` | string: `inline`, `alt`, `strip` | no |  |
| `query` | string | no |  |
| `highlights` | integer | no |  |
| `chunk` | object | no |  |
| `summary_sections` | boolean | no |  |
| `ai_prompt` | string | no | Natural-language instruction: the LLM turns the page into structured JSON under `payload.ai.data`. |
| `ai_schema` | object | no | JSON Schema the AI output must follow (deterministic shape). Either `ai_prompt` or `ai_schema` (or both) enables AI extraction. |
| `headers` | object \\| string | no | Extra headers replayed to the target. Either a JSON object (name → value) or a raw `Header: Value` block, one per line (lines starting with `//` or `#` are ignored). Max 32 headers, names must be RFC 7230 tokens, values ≤ 4096 characters; hop-by-hop and framing headers (host, content-length, transfer-encoding, connection, …) are rejected. |
| `cookies` | object | no | name → value cookies sent to the target. |
| `country` | string | no | Country code or place name: letters, digits, spaces, dots, hyphens, underscores; max 56 characters. `all` means no targeting. |
| `state` | string | no | Country code or place name: letters, digits, spaces, dots, hyphens, underscores; max 56 characters. `all` means no targeting. |
| `city` | string | no | Country code or place name: letters, digits, spaces, dots, hyphens, underscores; max 56 characters. `all` means no targeting. |
| `rotation` | string: `rotating`, `sticky` | no | New exit IP per request, or keep one IP for the session. Any other value is treated as `rotating`. Default `"rotating"`. |
| `sessionId` | string | no | Sticky session id (generated if omitted). |
| `sessionDuration` | number | no | Sticky session lifetime in minutes (clamped by the proxy layer, 3–1440, default 10). |

Example:

```json
{
  "url": "https://example.com/pricing",
  "format": "markdown",
  "country": "us",
  "extract": {
    "title": "h1",
    "price": ".price"
  }
}
```

##### Examples

**curl**

```bash
curl -X POST 'https://api.quantumproxies.io/v1/scraper/extract' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://example.com/pricing","format":"markdown","country":"us","extract":{"title":"h1","price":".price"}}'
```

**Python (requests)**

```python
import requests

r = requests.post(
    'https://api.quantumproxies.io/v1/scraper/extract',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    json={
        "url": "https://example.com/pricing",
        "format": "markdown",
        "country": "us",
        "extract": {
            "title": "h1",
            "price": ".price"
        }
    },
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/extract", {
  method: "POST",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "url": "https://example.com/pricing",
    "format": "markdown",
    "country": "us",
    "extract": {
      "title": "h1",
      "price": ".price"
    }
  })
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/extract');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY', 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode([
    'url' => 'https://example.com/pricing',
    'format' => 'markdown',
    'country' => 'us',
    'extract' => [
      'title' => 'h1',
      'price' => '.price'
    ]
  ]),
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Page fetched. `engine` says which tier served it and therefore which price applied.
- `400` — Malformed input. `message` names the parameter and the rule it broke.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `402` — The free tier plus wallet balance cannot cover the worst-case estimate of this call, or the account's monthly spend cap is reached. Nothing was run or charged.
- `403` — No proxy pool is available to serve the call (no house plan and no residential/datacenter/IPv6 plan on the account).
- `404` — `presetId` does not resolve to one of your presets.
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Extraction failed on our side or timed out (70 s budget). Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Extraction successful",
  "payload": {
    "url": "https://example.com/pricing",
    "finalUrl": "https://example.com/pricing",
    "status": 200,
    "contentType": "text/html; charset=utf-8",
    "format": "markdown",
    "title": "Pricing — Example",
    "metadata": {
      "description": "Simple, transparent pricing.",
      "canonical": "https://example.com/pricing",
      "language": "en"
    },
    "content": "# Pricing\n\nStarter — $9/month …",
    "data": {
      "title": "Pricing",
      "price": "$9"
    },
    "engine": "tls",
    "attempts": 1,
    "escalated": false,
    "bytes": 48213,
    "durationMs": 1240,
    "geo": {
      "country": "us",
      "state": null,
      "city": null,
      "rotation": "rotating"
    },
    "usage": {
      "cost_usd": 0.0002,
      "free_usd": 0.0002,
      "paid_usd": 0
    }
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Invalid format. Must be one of: markdown, html, text (or data_format markdown/screenshot, format raw)"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `402`:

```json
{
  "type": "error",
  "message": "Your free tier for this month is used up and your balance can't cover this call (estimated $0.002). Add pay-as-you-go credit or pick a plan — your API key stays the same: https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
  "payload": {
    "code": "INSUFFICIENT_FUNDS",
    "balance": 0,
    "free_remaining": 0,
    "estimated_cost": 0.002,
    "plans_url": "https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
    "billing_mode": "free_first"
  }
}
```

Example `403`:

```json
{
  "type": "error",
  "message": "Scraper API is not available: no proxy credentials found. Contact support or purchase a residential, datacenter or IPv6 plan."
}
```

Example `404`:

```json
{
  "type": "error",
  "message": "Preset not found",
  "payload": {
    "url": "https://example.com/"
  }
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Extraction timed out",
  "payload": {
    "url": "https://example.com/"
  }
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

### Search

Structured search-engine results (Google, Bing, DuckDuckGo) and async multi-page jobs.

#### Structured search results

`POST /scraper/serp`

**Price:** `serp` $0.0005 per call · `serp_render` $0.002 per call. Google (except autocomplete/reviews): `serp` when search_metadata.path is `http`, `serp_render` otherwise; Bing/DuckDuckGo: always `serp`. The pre-check reserves `serp_render` for Google. _(list of 2026-10-07)_  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
**Short alias:** `POST https://api.quantumproxies.io/v1/serp` (same handler, primary host only)  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/data-api.md)

One search on Google, Bing or DuckDuckGo through the residential pool, returned as JSON in the common SERP-API shape: organic results, ads, People Also Ask, related searches, knowledge panels, AI Overview and 17 Google verticals (`search_type`). Google renders by default so JS-only blocks are populated; pass `render: false` for the cheaper HTTP tier. Identical requests are served from a 24h cache for free (`search_metadata.from_cache`). It does not paginate on your behalf: request one `page` at a time and read `pagination.available_pages`/`has_next`, or use `/scraper/serp/bulk`. Billed per successful search by the tier that served it (`search_metadata.path`); blocked searches answer 429 and cost nothing; `autocomplete` and `reviews` always bill the request price.

##### Request body (required)

`Content-Type: application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `query` | string | no | Search query. Required except for ID-addressed verticals: `place_details` with `place_id`/`data_id`, `product` with `product_id`, `flights`, `lens`, `trends` (no query = trending searches), `reviews` with `data_id`. |
| `engine` | string: `google`, `bing`, `duckduckgo` | no |  Default `"google"`. |
| `search_type` | string: `search`, `shopping`, `images`, `news`, `places`, `maps`, `videos`, `scholar`, `jobs`, `autocomplete`, `place_details`, `hotels`, `flights`, `events`, `product`, `lens`, `reviews`, `trends` | no | Vertical. Alias `type`; Google aliases `tbm` (shop/isch/nws/lcl/vid) and `udm` (28/2/12/1/7) are mapped too. Default `"search"`. |
| `country` | string | no | Country code or place name: letters, digits, spaces, dots, hyphens, underscores; max 56 characters. `all` means no targeting. |
| `lang` | string | no | UI language, e.g. `en`, `it`. Alias `hl`. |
| `num` | number | no | Results to request (clamped to 1–100 by the service; Google serves ~10 per page and merges pages — see `search_metadata.paging`). |
| `page` | number | no | Result page, 1-based. |
| `start` | number | no | Result offset (alternative to `page`). |
| `device` | string: `desktop`, `mobile` | no | Alias `brd_mobile: 1`. Default `"desktop"`. |
| `render` | boolean | no | Google: renders by default; `false` pins the cheaper HTTP tier. Bing/DuckDuckGo never render by default. |
| `browser` | string: `chrome`, `firefox`, `safari` | no | Browser profile for the render tier. Alias `brd_browser`. |
| `safe` | string: `active`, `off` | no | SafeSearch. |
| `nfpr` | boolean \\| integer: `1` | no | Disable auto-corrected results. |
| `location` | string | no | Human-readable search location ("Milan, Italy"), encoded to uule server-side. |
| `uule` | string | no | Encoded uule token OR raw `lat,lon[,radius]`. |
| `google_params` | object | no | Escape hatch: extra Google URL params (keys ≤40 chars of letters, digits, `.`, `-`, `_`; values strings ≤512 chars or numbers). |
| `jobs` | boolean | no | Jobs box on the main SERP. Alias `ibp: "htl;jobs"`. |
| `place_id` | string | no | `place_details`. |
| `data_id` | string | no | `place_details` / `reviews` (feature id 0x…:0x…). |
| `product_id` | string \\| number | no | `product`: the seller list of one shopping result. |
| `departure_id` | string | no | `flights`: airport/city code. |
| `arrival_id` | string | no |  |
| `outbound_date` | string | no | YYYY-MM-DD. |
| `return_date` | string | no |  |
| `check_in_date` | string | no | `hotels`. |
| `check_out_date` | string | no |  |
| `adults` | number | no | `hotels`, clamped 1–30. |
| `children_ages` | array of number | no |  |
| `free_cancellation` | boolean | no |  |
| `accommodation_type` | string: `hotels`, `vacation_rentals` | no |  |
| `currency` | string | no | `hotels`/`flights` price currency (USD, EUR…). |
| `gps_coordinates` | string | no | `maps`: `lat,lon[,zoom]`. |
| `image_url` | string | no | `lens`: http(s) URL of the image to search by. |
| `exact_matches` | boolean | no | `lens`. |
| `sort_by` | string: `relevance`, `newest`, `highest_rating`, `lowest_rating` | no | `reviews`. |
| `filter` | string | no | `reviews`: keyword filter. |
| `next_page_token` | string | no | `reviews`: continuation token from `serpapi_pagination`. |
| `wait_for` | string | no | Render tier: CSS selector to wait for before capture. |
| `include_html` | boolean | no | Also return the page HTML under `html` (scripts stripped). |
| `product_ids` | boolean | no | `shopping`: resolve product ids for each result (slower). |

Example:

```json
{
  "query": "best coffee grinder",
  "engine": "google",
  "country": "us",
  "lang": "en",
  "num": 10
}
```

##### Examples

**curl**

```bash
curl -X POST 'https://api.quantumproxies.io/v1/scraper/serp' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"query":"best coffee grinder","engine":"google","country":"us","lang":"en","num":10}'
```

**Python (requests)**

```python
import requests

r = requests.post(
    'https://api.quantumproxies.io/v1/scraper/serp',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    json={
        "query": "best coffee grinder",
        "engine": "google",
        "country": "us",
        "lang": "en",
        "num": 10
    },
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/serp", {
  method: "POST",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "query": "best coffee grinder",
    "engine": "google",
    "country": "us",
    "lang": "en",
    "num": 10
  })
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/serp');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY', 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode([
    'query' => 'best coffee grinder',
    'engine' => 'google',
    'country' => 'us',
    'lang' => 'en',
    'num' => 10
  ]),
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Search succeeded. The envelope's `pagination` mirrors `payload.pagination`.
- `400` — Malformed input. `message` names the parameter and the rule it broke.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `402` — The free tier plus wallet balance cannot cover the worst-case estimate of this call, or the account's monthly spend cap is reached. Nothing was run or charged.
- `403` — No proxy pool is available to serve the call (no house plan and no residential/datacenter/IPv6 plan on the account).
- `429` — Key/plan rate limit, or the engine blocked every attempt (retryable, never billed).
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "SERP successful",
  "payload": {
    "search_metadata": {
      "status": "Success",
      "engine": "google",
      "search_url": "https://www.google.com/search?q=best+coffee+grinder&gl=us&hl=en",
      "created_at": "2026-10-07T09:12:44.000Z",
      "total_time_taken": 2.8,
      "attempts": 1,
      "bytes": 212544,
      "path": "http"
    },
    "search_parameters": {
      "engine": "google",
      "q": "best coffee grinder",
      "search_type": "search",
      "device": "desktop",
      "country": "us",
      "language": "en",
      "page": 1
    },
    "organic": [
      {
        "rank": 1,
        "title": "The 6 Best Coffee Grinders of 2026",
        "link": "https://example.com/reviews/coffee-grinders",
        "display_link": "example.com › reviews",
        "source": "example.com",
        "description": "We tested 24 burr grinders…",
        "date": "12 Sep 2026"
      }
    ],
    "ads": [],
    "people_also_ask": [
      {
        "question": "Is a burr grinder worth it?"
      }
    ],
    "related_searches": [
      {
        "query": "best coffee grinder under 100"
      }
    ],
    "ai_overview": null,
    "knowledge_graph": null,
    "pagination": {
      "current": 1,
      "next": 2,
      "total_pages": null,
      "other_pages": {
        "2": "https://www.google.com/search?q=best+coffee+grinder&start=10"
      },
      "available_pages": [
        1,
        2,
        3,
        4,
        5
      ],
      "has_next": true
    },
    "usage": {
      "cost_usd": 0.0005,
      "free_usd": 0.0005,
      "paid_usd": 0
    }
  },
  "pagination": {
    "current": 1,
    "next": 2,
    "total_pages": null,
    "available_pages": [
      1,
      2,
      3,
      4,
      5
    ],
    "has_next": true
  }
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Invalid format. Must be one of: markdown, html, text (or data_format markdown/screenshot, format raw)"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `402`:

```json
{
  "type": "error",
  "message": "Your free tier for this month is used up and your balance can't cover this call (estimated $0.002). Add pay-as-you-go credit or pick a plan — your API key stays the same: https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
  "payload": {
    "code": "INSUFFICIENT_FUNDS",
    "balance": 0,
    "free_remaining": 0,
    "estimated_cost": 0.002,
    "plans_url": "https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
    "billing_mode": "free_first"
  }
}
```

Example `403`:

```json
{
  "type": "error",
  "message": "Scraper API is not available: no proxy credentials found. Contact support or purchase a residential, datacenter or IPv6 plan."
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Start a multi-page search job

`POST /scraper/serp/bulk`

**Price:** `serp_render` $0.002 per call · `serp` $0.0005 per call. Google: `serp_render` × max_pages up front; Bing/DuckDuckGo: `serp` × max_pages. Unfetched pages refunded at settlement. _(list of 2026-10-07)_  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
**Short alias:** `POST https://api.quantumproxies.io/v1/serp/bulk` (same handler, primary host only)  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/data-api.md)

Paginates one query across up to 10 result pages asynchronously, riding real pagination on one session. Returns a job id immediately; poll `GET /scraper/serp/bulk/{jobId}` for the merged, de-duplicated organic results as pages land, or pass a `webhook` to receive the finished job. Only web-type verticals (search, news, videos, images, shopping) are supported here. Charged up front for `max_pages`; pages the query does not have are refunded when the job settles.

##### Request body (required)

`Content-Type: application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `query` | string | yes |  |
| `engine` | string: `google`, `bing`, `duckduckgo` | no |  Default `"google"`. |
| `search_type` | string: `search`, `news`, `videos`, `images`, `shopping` | no | Alias `type`. Default `"search"`. |
| `max_pages` | integer | no |  Default `5`. |
| `country` | string | no | Country code or place name: letters, digits, spaces, dots, hyphens, underscores; max 56 characters. `all` means no targeting. |
| `lang` | string | no | Alias `hl`. |
| `device` | string: `desktop`, `mobile` | no | Alias `brd_mobile: 1`. |
| `render` | boolean | no |  |
| `wait_for` | string | no |  |
| `browser` | string: `chrome`, `firefox`, `safari` | no |  |
| `safe` | string: `active`, `off` | no |  |
| `nfpr` | boolean \\| integer: `1` | no |  |
| `uule` | string | no |  |
| `location` | string | no |  |
| `google_params` | object | no |  |
| `webhook` | string | no | Public http(s) URL that receives the finished job by POST. |

Example:

```json
{
  "query": "best coffee grinder",
  "engine": "google",
  "country": "us",
  "max_pages": 3
}
```

##### Examples

**curl**

```bash
curl -X POST 'https://api.quantumproxies.io/v1/scraper/serp/bulk' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"query":"best coffee grinder","engine":"google","country":"us","max_pages":3}'
```

**Python (requests)**

```python
import requests

r = requests.post(
    'https://api.quantumproxies.io/v1/scraper/serp/bulk',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    json={
        "query": "best coffee grinder",
        "engine": "google",
        "country": "us",
        "max_pages": 3
    },
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/serp/bulk", {
  method: "POST",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "query": "best coffee grinder",
    "engine": "google",
    "country": "us",
    "max_pages": 3
  })
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/serp/bulk');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY', 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode([
    'query' => 'best coffee grinder',
    'engine' => 'google',
    'country' => 'us',
    'max_pages' => 3
  ]),
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Job accepted.
- `400` — Malformed input. `message` names the parameter and the rule it broke.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `402` — The free tier plus wallet balance cannot cover the worst-case estimate of this call, or the account's monthly spend cap is reached. Nothing was run or charged.
- `403` — No proxy pool is available to serve the call (no house plan and no residential/datacenter/IPv6 plan on the account).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Bulk SERP started",
  "payload": {
    "id": "sb_4d1e9c",
    "status": "running",
    "query": "best coffee grinder",
    "total": 3,
    "completed": 0,
    "statusUrl": "/api/v1/scraper/serp/bulk/sb_4d1e9c",
    "usage": {
      "cost_usd": 0.006,
      "free_usd": 0.006,
      "paid_usd": 0
    }
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Invalid format. Must be one of: markdown, html, text (or data_format markdown/screenshot, format raw)"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `402`:

```json
{
  "type": "error",
  "message": "Your free tier for this month is used up and your balance can't cover this call (estimated $0.002). Add pay-as-you-go credit or pick a plan — your API key stays the same: https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
  "payload": {
    "code": "INSUFFICIENT_FUNDS",
    "balance": 0,
    "free_remaining": 0,
    "estimated_cost": 0.002,
    "plans_url": "https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
    "billing_mode": "free_first"
  }
}
```

Example `403`:

```json
{
  "type": "error",
  "message": "Scraper API is not available: no proxy credentials found. Contact support or purchase a residential, datacenter or IPv6 plan."
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Poll a multi-page search job

`GET /scraper/serp/bulk/{jobId}`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
**Short alias:** `GET https://api.quantumproxies.io/v1/serp/bulk/{jobId}` (same handler, primary host only)  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/data-api.md)

Progress and merged organic results of a bulk search job you own. Pass the previous response's `nextCursor` as `since` to receive only new rows. Jobs are kept about an hour after they finish. Free to poll.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `jobId` | path | string | yes | Job id returned by the POST that started it. |
| `since` | query | integer | no | Organic cursor from the previous poll's `nextCursor`. |

##### Examples

**curl**

```bash
curl -X GET 'https://api.quantumproxies.io/v1/scraper/serp/bulk/job_8f2c1a' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.get(
    'https://api.quantumproxies.io/v1/scraper/serp/bulk/job_8f2c1a',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/serp/bulk/job_8f2c1a", {
  method: "GET",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/serp/bulk/job_8f2c1a');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Job view.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `404` — Unknown id — or an id that belongs to another account (ownership failures answer 404, never 403, so ids cannot be probed).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Bulk SERP status",
  "payload": {
    "id": "sb_4d1e9c",
    "status": "completed",
    "query": "best coffee grinder",
    "total": 3,
    "completed": 3,
    "failed": 0,
    "pages": [
      {
        "page": 1,
        "organic_count": 10,
        "bytes": 210331,
        "path": "http"
      },
      {
        "page": 2,
        "organic_count": 10,
        "bytes": 198002,
        "path": "http"
      },
      {
        "page": 3,
        "organic_count": 9,
        "bytes": 190114,
        "path": "http"
      }
    ],
    "organic": [
      {
        "rank": 1,
        "title": "The 6 Best Coffee Grinders of 2026",
        "link": "https://example.com/reviews/coffee-grinders",
        "display_link": "example.com",
        "description": "…"
      }
    ],
    "related_searches": [],
    "ai_overview": null,
    "nextCursor": 29,
    "createdAt": 1759828364000,
    "finishedAt": 1759828391000
  },
  "pagination": {}
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `404`:

```json
{
  "type": "error",
  "message": "Job not found"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Cancel a multi-page search job

`DELETE /scraper/serp/bulk/{jobId}`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
**Short alias:** `DELETE https://api.quantumproxies.io/v1/serp/bulk/{jobId}` (same handler, primary host only)  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/data-api.md)

Stops a running bulk search job you own. Pages never fetched are refunded at settlement.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `jobId` | path | string | yes | Job id returned by the POST that started it. |

##### Examples

**curl**

```bash
curl -X DELETE 'https://api.quantumproxies.io/v1/scraper/serp/bulk/job_8f2c1a' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.delete(
    'https://api.quantumproxies.io/v1/scraper/serp/bulk/job_8f2c1a',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/serp/bulk/job_8f2c1a", {
  method: "DELETE",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/serp/bulk/job_8f2c1a');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'DELETE',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Cancelled.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `404` — Unknown id — or an id that belongs to another account (ownership failures answer 404, never 403, so ids cannot be probed).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Bulk SERP cancelled",
  "payload": {
    "id": "sb_4d1e9c",
    "status": "cancelled"
  },
  "pagination": {}
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `404`:

```json
{
  "type": "error",
  "message": "Job not found"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

### Map & Crawl

URL discovery for a whole site and asynchronous site crawls.

#### Discover a site's URLs

`POST /scraper/map`

**Price:** `map` $0.0005 per call _(list of 2026-10-07)_  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
**Short alias:** `POST https://api.quantumproxies.io/v1/map` (same handler, primary host only)  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/data-api.md)

Merges robots.txt sitemaps, /sitemap.xml (nested indexes included) and same-domain homepage links into one de-duplicated list, synchronously. Returns up to `limit` URLs plus the site-wide `total` and a per-section `summary`; `group_by: path` returns the path tree instead of the list. It does not fetch page bodies — pair it with /scraper/batch for that. One flat price per call.

##### Request body (required)

`Content-Type: application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `url` | string | yes | Seed URL. |
| `limit` | number | no | Max URLs to return (service cap 5000). Default `100`. |
| `search` | string | no | Only return URLs containing this substring. |
| `includeSubdomains` | boolean | no |  Default `false`. |
| `sitemapOnly` | boolean | no | Skip the homepage link scrape. Default `false`. |
| `group_by` | string: `path` | no | Return the path tree with counts instead of the URL list. Alias `groupBy`. |
| `country` | string | no | Country code or place name: letters, digits, spaces, dots, hyphens, underscores; max 56 characters. `all` means no targeting. |

Example:

```json
{
  "url": "https://example.com",
  "limit": 100,
  "search": "blog"
}
```

##### Examples

**curl**

```bash
curl -X POST 'https://api.quantumproxies.io/v1/scraper/map' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://example.com","limit":100,"search":"blog"}'
```

**Python (requests)**

```python
import requests

r = requests.post(
    'https://api.quantumproxies.io/v1/scraper/map',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    json={
        "url": "https://example.com",
        "limit": 100,
        "search": "blog"
    },
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/map", {
  method: "POST",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "url": "https://example.com",
    "limit": 100,
    "search": "blog"
  })
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/map');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY', 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode([
    'url' => 'https://example.com',
    'limit' => 100,
    'search' => 'blog'
  ]),
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — URLs discovered.
- `400` — Malformed input. `message` names the parameter and the rule it broke.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `402` — The free tier plus wallet balance cannot cover the worst-case estimate of this call, or the account's monthly spend cap is reached. Nothing was run or charged.
- `403` — No proxy pool is available to serve the call (no house plan and no residential/datacenter/IPv6 plan on the account).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Map successful",
  "payload": {
    "url": "https://example.com",
    "links": [
      "https://example.com/blog/",
      "https://example.com/blog/how-to-scrape"
    ],
    "count": 100,
    "total": 2417,
    "summary": {
      "/blog": 1988,
      "/docs": 240,
      "/": 1
    },
    "sources": {
      "sitemap": 2410,
      "homepage": 42
    },
    "durationMs": 3120,
    "geo": {
      "country": null
    },
    "usage": {
      "cost_usd": 0.0005,
      "free_usd": 0.0005,
      "paid_usd": 0
    }
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Invalid format. Must be one of: markdown, html, text (or data_format markdown/screenshot, format raw)"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `402`:

```json
{
  "type": "error",
  "message": "Your free tier for this month is used up and your balance can't cover this call (estimated $0.002). Add pay-as-you-go credit or pick a plan — your API key stays the same: https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
  "payload": {
    "code": "INSUFFICIENT_FUNDS",
    "balance": 0,
    "free_remaining": 0,
    "estimated_cost": 0.002,
    "plans_url": "https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
    "billing_mode": "free_first"
  }
}
```

Example `403`:

```json
{
  "type": "error",
  "message": "Scraper API is not available: no proxy credentials found. Contact support or purchase a residential, datacenter or IPv6 plan."
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Start a site crawl

`POST /scraper/crawl`

**Price:** `crawl_page` $0.0003 per call · `extract_render` $0.001 per call + $4/GB, max $0.05 per call. `crawl_page` × min(limit, 500) up front, or `extract_render` × pages when render is true. Refund of the unfetched share at settlement. _(list of 2026-10-07)_  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
**Short alias:** `POST https://api.quantumproxies.io/v1/crawl` (same handler, primary host only)  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/data-api.md)

Breadth-first crawl from a seed URL through the residential pool, each page converted to the requested format. Returns a job id immediately; poll `GET /scraper/crawl/{jobId}`. Charged up front on `limit` pages (render price per page with `render: true`); unfetched or failed pages are refunded when the job settles. A render crawl takes one browser-render token at submission and its pages then queue on the plan's render concurrency.

##### Request body (required)

`Content-Type: application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `url` | string | yes | Seed URL. |
| `limit` | number | no | Max pages (cap 500). Billed on this up front. Default `50`. |
| `depth` | number | no | Max link depth from the seed (cap 10). Default `3`. |
| `format` | string: `markdown`, `html`, `text` | no |  Default `"markdown"`. |
| `contentMode` | string: `smart`, `article`, `full` | no | Alias `content_mode`. Default `"smart"`. |
| `render` | boolean | no | Render every page with the stealth browser (slower, rendered rate). Default `false`. |
| `sameDomain` | boolean | no |  Default `true`. |
| `allowSubdomains` | boolean | no |  Default `false`. |
| `include` | array of string | no | URL substrings/globs to include, e.g. ["/guides/*"]. |
| `exclude` | array of string | no |  |
| `country` | string | no | Country code or place name: letters, digits, spaces, dots, hyphens, underscores; max 56 characters. `all` means no targeting. |

Example:

```json
{
  "url": "https://docs.example.com",
  "limit": 50,
  "depth": 3,
  "include": [
    "/guides/*"
  ]
}
```

##### Examples

**curl**

```bash
curl -X POST 'https://api.quantumproxies.io/v1/scraper/crawl' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://docs.example.com","limit":50,"depth":3,"include":["/guides/*"]}'
```

**Python (requests)**

```python
import requests

r = requests.post(
    'https://api.quantumproxies.io/v1/scraper/crawl',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    json={
        "url": "https://docs.example.com",
        "limit": 50,
        "depth": 3,
        "include": [
            "/guides/*"
        ]
    },
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/crawl", {
  method: "POST",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "url": "https://docs.example.com",
    "limit": 50,
    "depth": 3,
    "include": [
      "/guides/*"
    ]
  })
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/crawl');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY', 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode([
    'url' => 'https://docs.example.com',
    'limit' => 50,
    'depth' => 3,
    'include' => ['/guides/*']
  ]),
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Crawl started.
- `400` — Malformed input. `message` names the parameter and the rule it broke.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `402` — The free tier plus wallet balance cannot cover the worst-case estimate of this call, or the account's monthly spend cap is reached. Nothing was run or charged.
- `403` — No proxy pool is available to serve the call (no house plan and no residential/datacenter/IPv6 plan on the account).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Crawl started",
  "payload": {
    "id": "cr_d5b8a1",
    "status": "running",
    "seed": "https://docs.example.com/",
    "limit": 50,
    "depth": 3,
    "format": "markdown",
    "pagesCrawled": 0,
    "pagesQueued": 1,
    "statusUrl": "/api/v1/scraper/crawl/cr_d5b8a1",
    "usage": {
      "cost_usd": 0.015,
      "free_usd": 0.015,
      "paid_usd": 0
    }
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Invalid format. Must be one of: markdown, html, text (or data_format markdown/screenshot, format raw)"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `402`:

```json
{
  "type": "error",
  "message": "Your free tier for this month is used up and your balance can't cover this call (estimated $0.002). Add pay-as-you-go credit or pick a plan — your API key stays the same: https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
  "payload": {
    "code": "INSUFFICIENT_FUNDS",
    "balance": 0,
    "free_remaining": 0,
    "estimated_cost": 0.002,
    "plans_url": "https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
    "billing_mode": "free_first"
  }
}
```

Example `403`:

```json
{
  "type": "error",
  "message": "Scraper API is not available: no proxy credentials found. Contact support or purchase a residential, datacenter or IPv6 plan."
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Poll a crawl job

`GET /scraper/crawl/{jobId}`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
**Short alias:** `GET https://api.quantumproxies.io/v1/crawl/{jobId}` (same handler, primary host only)  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/data-api.md)

Progress and crawled pages of a job you own. Use `since` (the previous `nextCursor`) to page a large crawl and `include_content=false` for cheap status checks. Jobs are kept about an hour after they finish. Free to poll.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `jobId` | path | string | yes | Job id returned by the POST that started it. |
| `since` | query | integer | no | Page cursor from the previous poll's `nextCursor`. |
| `include_content` | query | string: `true`, `false` | no | Omit to get the full job; `false` strips page content. |

##### Examples

**curl**

```bash
curl -X GET 'https://api.quantumproxies.io/v1/scraper/crawl/job_8f2c1a' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.get(
    'https://api.quantumproxies.io/v1/scraper/crawl/job_8f2c1a',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/crawl/job_8f2c1a", {
  method: "GET",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/crawl/job_8f2c1a');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Job view.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `404` — Unknown id — or an id that belongs to another account (ownership failures answer 404, never 403, so ids cannot be probed).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Crawl status",
  "payload": {
    "id": "cr_d5b8a1",
    "status": "completed",
    "seed": "https://docs.example.com/",
    "limit": 50,
    "depth": 3,
    "format": "markdown",
    "pagesCrawled": 37,
    "pagesQueued": 0,
    "pages": [
      {
        "url": "https://docs.example.com/guides/getting-started",
        "status": 200,
        "title": "Getting started",
        "depth": 1,
        "content": "# Getting started\n…"
      }
    ],
    "nextCursor": 37,
    "hasMore": false,
    "createdAt": 1759828364000,
    "finishedAt": 1759828512000
  },
  "pagination": {}
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `404`:

```json
{
  "type": "error",
  "message": "Job not found"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Cancel a crawl job

`DELETE /scraper/crawl/{jobId}`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
**Short alias:** `DELETE https://api.quantumproxies.io/v1/crawl/{jobId}` (same handler, primary host only)  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/data-api.md)

Stops a running crawl you own; pages never fetched are refunded at settlement.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `jobId` | path | string | yes | Job id returned by the POST that started it. |

##### Examples

**curl**

```bash
curl -X DELETE 'https://api.quantumproxies.io/v1/scraper/crawl/job_8f2c1a' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.delete(
    'https://api.quantumproxies.io/v1/scraper/crawl/job_8f2c1a',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/crawl/job_8f2c1a", {
  method: "DELETE",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/crawl/job_8f2c1a');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'DELETE',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Cancelled.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `404` — Unknown id — or an id that belongs to another account (ownership failures answer 404, never 403, so ids cannot be probed).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Crawl cancelled",
  "payload": {
    "id": "cr_d5b8a1",
    "status": "cancelled"
  },
  "pagination": {}
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `404`:

```json
{
  "type": "error",
  "message": "Job not found"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

### Batch

Many known URLs scraped asynchronously.

#### Scrape many URLs asynchronously

`POST /scraper/batch`

**Price:** `batch_url` $0.0002 per call · `extract_render` $0.001 per call + $4/GB, max $0.05 per call. `batch_url` × urls up front (or `extract_render` × urls when rendering). Failed URLs refunded at settlement. _(list of 2026-10-07)_  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
**Short alias:** `POST https://api.quantumproxies.io/v1/batch` (same handler, primary host only)  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/data-api.md)

Up to 5,000 URLs you already know, with shared options, run concurrently through the residential pool. Returns a job id immediately; poll `GET /scraper/batch/{jobId}` or pass a `webhook` to receive the finished job by POST. Charged up front per URL (render price with `render: true` or `engine: render`); the failed/blocked share is refunded when the job settles. A render batch takes one browser-render token at submission.

##### Request body (required)

`Content-Type: application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `urls` | array of string | yes |  |
| `format` | string: `markdown`, `html`, `text` | no |  Default `"markdown"`. |
| `engine` | string: `auto`, `tls`, `fetch`, `render` | no |  Default `"auto"`. |
| `render` | boolean | no | Force the headless browser for every URL. Default `false`. |
| `extract` | object (ExtractSchema) | no | Structured-extraction schema: field name → CSS selector, or an object with `selector`, optional `attr` (attribute to read instead of text) and `all` (true = every match as an array). |
| `contentMode` | string: `smart`, `article`, `full` | no | Alias `content_mode`. Default `"smart"`. |
| `fullPage` | boolean | no | Legacy: contentMode full. |
| `mode` | string: `full`, `summary` | no | `summary` stores per-URL metadata only (title, description, canonical, contentLength). Default `"full"`. |
| `concurrency` | number | no | Simultaneous fetches (clamped 1–20; your tier's batch concurrency also applies). Default `5`. |
| `webhook` | string | no | Public http(s) URL that receives the finished job by POST. |
| `country` | string | no | Country code or place name: letters, digits, spaces, dots, hyphens, underscores; max 56 characters. `all` means no targeting. |

Example:

```json
{
  "urls": [
    "https://example.com/a",
    "https://example.com/b"
  ],
  "format": "markdown",
  "mode": "summary"
}
```

##### Examples

**curl**

```bash
curl -X POST 'https://api.quantumproxies.io/v1/scraper/batch' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"urls":["https://example.com/a","https://example.com/b"],"format":"markdown","mode":"summary"}'
```

**Python (requests)**

```python
import requests

r = requests.post(
    'https://api.quantumproxies.io/v1/scraper/batch',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    json={
        "urls": [
            "https://example.com/a",
            "https://example.com/b"
        ],
        "format": "markdown",
        "mode": "summary"
    },
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/batch", {
  method: "POST",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "urls": [
      "https://example.com/a",
      "https://example.com/b"
    ],
    "format": "markdown",
    "mode": "summary"
  })
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/batch');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY', 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode([
    'urls' => ['https://example.com/a', 'https://example.com/b'],
    'format' => 'markdown',
    'mode' => 'summary'
  ]),
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Batch started.
- `400` — Malformed input. `message` names the parameter and the rule it broke.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `402` — The free tier plus wallet balance cannot cover the worst-case estimate of this call, or the account's monthly spend cap is reached. Nothing was run or charged.
- `403` — No proxy pool is available to serve the call (no house plan and no residential/datacenter/IPv6 plan on the account).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Batch started",
  "payload": {
    "id": "bt_91ac07",
    "status": "running",
    "total": 2,
    "completed": 0,
    "failed": 0,
    "statusUrl": "/api/v1/scraper/batch/bt_91ac07",
    "usage": {
      "cost_usd": 0.0004,
      "free_usd": 0.0004,
      "paid_usd": 0
    }
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Invalid format. Must be one of: markdown, html, text (or data_format markdown/screenshot, format raw)"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `402`:

```json
{
  "type": "error",
  "message": "Your free tier for this month is used up and your balance can't cover this call (estimated $0.002). Add pay-as-you-go credit or pick a plan — your API key stays the same: https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
  "payload": {
    "code": "INSUFFICIENT_FUNDS",
    "balance": 0,
    "free_remaining": 0,
    "estimated_cost": 0.002,
    "plans_url": "https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
    "billing_mode": "free_first"
  }
}
```

Example `403`:

```json
{
  "type": "error",
  "message": "Scraper API is not available: no proxy credentials found. Contact support or purchase a residential, datacenter or IPv6 plan."
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Poll a batch job

`GET /scraper/batch/{jobId}`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
**Short alias:** `GET https://api.quantumproxies.io/v1/batch/{jobId}` (same handler, primary host only)  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/data-api.md)

Progress and per-URL results of a job you own. Polls return metadata only unless `include_content=true`; pass the previous `nextCursor` as `since` to receive only newer items. Jobs are kept about an hour after they finish. Free to poll.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `jobId` | path | string | yes | Job id returned by the POST that started it. |
| `since` | query | integer | no | Item cursor from the previous poll's `nextCursor`. |
| `include_content` | query | string: `true`, `false` | no | `true` includes each item's page content. |

##### Examples

**curl**

```bash
curl -X GET 'https://api.quantumproxies.io/v1/scraper/batch/job_8f2c1a' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.get(
    'https://api.quantumproxies.io/v1/scraper/batch/job_8f2c1a',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/batch/job_8f2c1a", {
  method: "GET",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/batch/job_8f2c1a');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Job view.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `404` — Unknown id — or an id that belongs to another account (ownership failures answer 404, never 403, so ids cannot be probed).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Batch status",
  "payload": {
    "id": "bt_91ac07",
    "status": "completed",
    "total": 2,
    "completed": 2,
    "failed": 0,
    "contentTruncated": false,
    "items": [
      {
        "url": "https://example.com/a",
        "status": 200,
        "title": "Page A",
        "description": "…",
        "canonical": "https://example.com/a",
        "contentLength": 5120,
        "engine": "tls",
        "bytes": 31870
      },
      {
        "url": "https://example.com/b",
        "status": 200,
        "title": "Page B",
        "description": null,
        "canonical": null,
        "contentLength": 2210,
        "engine": "tls",
        "bytes": 12003
      }
    ],
    "nextCursor": 2,
    "hasMore": false,
    "createdAt": 1759828364000,
    "finishedAt": 1759828370000
  },
  "pagination": {}
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `404`:

```json
{
  "type": "error",
  "message": "Job not found"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Cancel a batch job

`DELETE /scraper/batch/{jobId}`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
**Short alias:** `DELETE https://api.quantumproxies.io/v1/batch/{jobId}` (same handler, primary host only)  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/data-api.md)

Stops a running batch you own; URLs never fetched are refunded at settlement.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `jobId` | path | string | yes | Job id returned by the POST that started it. |

##### Examples

**curl**

```bash
curl -X DELETE 'https://api.quantumproxies.io/v1/scraper/batch/job_8f2c1a' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.delete(
    'https://api.quantumproxies.io/v1/scraper/batch/job_8f2c1a',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/batch/job_8f2c1a", {
  method: "DELETE",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/batch/job_8f2c1a');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'DELETE',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Cancelled.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `404` — Unknown id — or an id that belongs to another account (ownership failures answer 404, never 403, so ids cannot be probed).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Batch cancelled",
  "payload": {
    "id": "bt_91ac07",
    "status": "cancelled"
  },
  "pagination": {}
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `404`:

```json
{
  "type": "error",
  "message": "Job not found"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

### Web Unlocker

Replay any HTTP request through a residential exit with a real browser fingerprint; prepaid per GB.

#### Replay a request through the Web Unlocker

`POST /scraper/unlock`

**Price:** `unlock_request` $2.40/GB of transferred data. Flat base is 0; the whole charge is the `unlock_request` perGb meter applied to `usage.bytes`, debited from the prepaid unlocker GB of the chosen tier. No wallet charge, no free tier. 402 when the tier's GB are exhausted or expired. _(list of 2026-10-07)_  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/data-api.md)

Send a request the way your own HTTP client would (url, method, headers, body) and get it back replayed through a residential (or mobile) exit under a real browser TLS fingerprint. GET/HEAD/OPTIONS retry on a fresh exit IP under a different fingerprint when blocked and finally escalate to a headless browser that answers JavaScript challenges; any other method gets exactly one attempt (a response means the target saw it, and replaying a POST could double-submit). A still-blocked page is returned with `blocked: true`, `blockClass` and `vendor`, never as a silent success; `failOnBlock: true` turns that into a 502. It does not solve interactive captchas. This is the same engine as the CONNECT forward proxy, without the proxy protocol — no certificate to install. Billing is different from every other Data API call: it is metered in bytes against the tier's prepaid Web Unlocker GB (`premium` = residential exits, `mobile` = mobile exits), not the wallet, and every attempt counts — retries, a blocked page, the browser escalation's page load.

##### Request body (required)

`Content-Type: application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `url` | string | yes |  |
| `method` | string | no |  Default `"GET"`. |
| `headers` | object \\| string | no | Extra headers replayed to the target. Either a JSON object (name → value) or a raw `Header: Value` block, one per line (lines starting with `//` or `#` are ignored). Max 32 headers, names must be RFC 7230 tokens, values ≤ 4096 characters; hop-by-hop and framing headers (host, content-length, transfer-encoding, connection, …) are rejected. |
| `body` | string | no | Request body as UTF-8 text (JSON, form data…). Use this OR `bodyBase64`. |
| `bodyBase64` | string | no | Request body as base64 for binary payloads. Max 8 MB decoded. |
| `tier` | string: `premium`, `mobile` | no | Which prepaid pool pays and which exits are used. Anything other than `mobile` is `premium`. Default `"premium"`. |
| `tlsProfile` | string: `chrome`, `firefox`, `safari`, `safari_ios`, `edge`, `brave`, `mobile` | no |  Default `"chrome"`. |
| `mobile` | boolean | no | Mobile Safari fingerprint (fingerprint only — the product tier is `tier`). Default `false`. |
| `render` | string: `html`, `png` \\| boolean | no | `html`/`png` (or `true` = html) runs the page in a headless browser instead of the TLS tier, GET/HEAD only, one render token. `false` pins the TLS tier: a blocked page is returned as-is, never escalated. |
| `autoRender` | boolean | no | Escalate a blocked GET to the browser. Default `true`. |
| `keepHeaders` | boolean | no |  Default `false`. |
| `successStatusCodes` | array of integer | no | Origin statuses to accept as success — never treated as a block, never retried. |
| `timeoutMs` | integer | no | Per-attempt timeout at the target (capped by the service's 90 s total budget). |
| `failOnBlock` | boolean | no | 502 instead of a 200 with `blocked: true`. GB are debited either way. Default `false`. |
| `waitForSelector` | string | no | Browser tier: CSS selector to wait for before capture. |
| `waitMs` | integer | no |  |
| `returnCookies` | boolean | no | Return the origin's cookie jar under `cookies`. Default `false`. |
| `cookies` | object | no | Cookies merged into the Cookie header sent to the target (e.g. a clearance obtained earlier). |
| `country` | string | no | Country code or place name: letters, digits, spaces, dots, hyphens, underscores; max 56 characters. `all` means no targeting. |
| `state` | string | no | Country code or place name: letters, digits, spaces, dots, hyphens, underscores; max 56 characters. `all` means no targeting. |
| `city` | string | no | Country code or place name: letters, digits, spaces, dots, hyphens, underscores; max 56 characters. `all` means no targeting. |
| `rotation` | string: `rotating`, `sticky` | no |  Default `"rotating"`. |
| `sessionId` | string | no | Sticky session id — reuse it across calls to keep one exit IP. |
| `sessionDuration` | number | no | Sticky session lifetime in minutes. |

Example:

```json
{
  "url": "https://example.com/api/search?q=shoes",
  "method": "GET",
  "headers": {
    "Accept": "application/json"
  },
  "country": "us",
  "tier": "premium"
}
```

##### Examples

**curl**

```bash
curl -X POST 'https://api.quantumproxies.io/v1/scraper/unlock' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://example.com/api/search?q=shoes","method":"GET","headers":{"Accept":"application/json"},"country":"us","tier":"premium"}'
```

**Python (requests)**

```python
import requests

r = requests.post(
    'https://api.quantumproxies.io/v1/scraper/unlock',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    json={
        "url": "https://example.com/api/search?q=shoes",
        "method": "GET",
        "headers": {
            "Accept": "application/json"
        },
        "country": "us",
        "tier": "premium"
    },
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/unlock", {
  method: "POST",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "url": "https://example.com/api/search?q=shoes",
    "method": "GET",
    "headers": {
      "Accept": "application/json"
    },
    "country": "us",
    "tier": "premium"
  })
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/unlock');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY', 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode([
    'url' => 'https://example.com/api/search?q=shoes',
    'method' => 'GET',
    'headers' => [
      'Accept' => 'application/json'
    ],
    'country' => 'us',
    'tier' => 'premium'
  ]),
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — The origin's response (possibly a block page, see `blocked`).
- `400` — Malformed input. `message` names the parameter and the rule it broke.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `402` — The tier's prepaid unlocker GB are exhausted or expired.
- `403` — No exit pool for the requested tier (e.g. no mobile unlocker GB on the account).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `502` — Only with `failOnBlock: true`: every tier came back blocked. Same diagnostics as the 200 form; the GB moved are debited.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Unlocker request successful",
  "payload": {
    "status": 200,
    "headers": {
      "content-type": "application/json; charset=utf-8",
      "cache-control": "no-store"
    },
    "body": "{\"results\":[{\"id\":1,\"name\":\"Runner\"}]}",
    "bodyBase64": "eyJyZXN1bHRzIjpbeyJpZCI6MSwibmFtZSI6IlJ1bm5lciJ9XX0=",
    "finalUrl": "https://example.com/api/search?q=shoes",
    "contentType": "application/json; charset=utf-8",
    "profile": "chrome",
    "attempts": 1,
    "blocked": false,
    "clearance": "miss",
    "exitSessionId": "s_2f91",
    "strategy": {
      "startTier": "tls",
      "hedged": false
    },
    "rendered": false,
    "escalated": false,
    "tier": "premium",
    "geo": {
      "country": "us",
      "state": null,
      "city": null,
      "rotation": "rotating"
    },
    "usage": {
      "bytes": 18432,
      "unlock_gb_remaining": 4.98,
      "unlock_gb_purchased": 5
    }
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Invalid format. Must be one of: markdown, html, text (or data_format markdown/screenshot, format raw)"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `402`:

```json
{
  "type": "error",
  "message": "Web Unlocker premium bandwidth exhausted. Buy more unlocker GB to keep using it.",
  "payload": {
    "tier": "premium",
    "unlock_gb_remaining": 0,
    "unlock_gb_purchased": 5,
    "expires_at": "2026-11-01T00:00:00.000Z",
    "expired": false
  }
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `502`:

```json
{
  "type": "error",
  "message": "Target blocked the request: js_challenge",
  "payload": {
    "status": 403,
    "blockReason": "js_challenge",
    "blockClass": "js_challenge",
    "vendor": "cloudflare",
    "attempts": 3,
    "rendered": true,
    "escalated": true,
    "usage": {
      "bytes": 90112,
      "unlock_gb_remaining": 4.97,
      "unlock_gb_purchased": 5
    }
  }
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Download the unlocker CA certificate

`GET /scraper/unlock/ca`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/data-api.md)

The interception CA used by the Web Unlocker forward proxy (CONNECT mode). The proxy terminates your TLS to read and rewrite the request, so an HTTP client that keeps certificate verification on must trust this file for the target host (`curl --proxy-cacert qp-unlocker-ca.pem --cacert qp-unlocker-ca.pem …`). Not needed for POST /scraper/unlock, nor for the proxy's direct mode. Public by nature, kept behind the key so downloads are attributable. Free.

##### Examples

**curl**

```bash
curl -X GET 'https://api.quantumproxies.io/v1/scraper/unlock/ca' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.get(
    'https://api.quantumproxies.io/v1/scraper/unlock/ca',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
r.raise_for_status()
open("response.out", "wb").write(r.content)
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/unlock/ca", {
  method: "GET",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const bytes = Buffer.from(await res.arrayBuffer());
require("node:fs").writeFileSync("response.out", bytes);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/unlock/ca');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
file_put_contents('response.out', $raw);
```

##### Responses

- `200` — PEM certificate (attachment `qp-unlocker-ca.pem`, cacheable 1h).
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `503` — The certificate could not be fetched from the unlocker service right now.

Example `200` response:

```json
"-----BEGIN CERTIFICATE-----\nMIIB…\n-----END CERTIFICATE-----\n"
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "Unlocker certificate is temporarily unavailable"
}
```

Try it: https://app.quantumproxies.io/data-api/playground

## AI

Markdown for this group only: https://quantumproxies.io/docs/ai.md

### AI extraction

Natural-language agents that drive the scraper and return JSON.

#### Natural-language scraping agent

`POST /scraper/ai`

**Price:** `ai_extract` $0.001 per call, max $1.50 per call · `extract` $0.0002 per call + $3/GB, max $0.02 per call · `serp` $0.0005 per call · `map` $0.0005 per call. ai_extract + steps × extract + searches × serp + mapped × map + tokens × ai_token_markup, capped at meters.ai_extract.capUsd. _(list of 2026-10-07)_  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/ai.md)

Describe a task in plain English; a model drives the scraper API as a tool — formats the request, fetches the page(s) through residential proxies, can search and map when the task needs it, and returns the extracted JSON under `data` with the pages it visited under `steps`. Use it when you do not want to write selectors or a schema; use /scraper/extract with `extract`/`ai_schema` when you already know the page. Not deterministic and not the cheapest path for repeated layouts (see /scraper/parser/generate). Billed as the `ai_extract` base fee plus one `extract` per page fetched, `serp` per search, `map` per map, plus LLM tokens × ai_token_markup, capped at the `ai_extract` meter's capUsd; the pre-check reserves that cap.

##### Request body (required)

`Content-Type: application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `task` | string | yes | Plain-English instruction, e.g. "get every product with its name and price from this page". |
| `url` | string | no | Starting URL, if the task does not already contain one. |

Example:

```json
{
  "task": "Get every plan and its monthly price",
  "url": "https://example.com/pricing"
}
```

##### Examples

**curl**

```bash
curl -X POST 'https://api.quantumproxies.io/v1/scraper/ai' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"task":"Get every plan and its monthly price","url":"https://example.com/pricing"}'
```

**Python (requests)**

```python
import requests

r = requests.post(
    'https://api.quantumproxies.io/v1/scraper/ai',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    json={
        "task": "Get every plan and its monthly price",
        "url": "https://example.com/pricing"
    },
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/ai", {
  method: "POST",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "task": "Get every plan and its monthly price",
    "url": "https://example.com/pricing"
  })
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/ai');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY', 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode([
    'task' => 'Get every plan and its monthly price',
    'url' => 'https://example.com/pricing'
  ]),
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Extraction finished.
- `400` — Missing task, or the agent reported a client-side problem (bad URL, page unusable).
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `402` — The free tier plus wallet balance cannot cover the worst-case estimate of this call, or the account's monthly spend cap is reached. Nothing was run or charged.
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Agent failure or the AI agent is not configured on this instance. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "AI extraction successful",
  "payload": {
    "data": {
      "plans": [
        {
          "name": "Starter",
          "price_usd": 9
        },
        {
          "name": "Growth",
          "price_usd": 29
        }
      ]
    },
    "steps": [
      {
        "url": "https://example.com/pricing",
        "status": 200,
        "engine": "tls"
      }
    ],
    "searches": 0,
    "mapped": 0,
    "model": "gpt-4o-mini",
    "bytes": 48213,
    "usage": {
      "input_tokens": 3120,
      "output_tokens": 180,
      "cost_usd": 0.0019,
      "free_usd": 0.0019,
      "paid_usd": 0
    }
  },
  "pagination": {}
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `402`:

```json
{
  "type": "error",
  "message": "Your free tier for this month is used up and your balance can't cover this call (estimated $0.002). Add pay-as-you-go credit or pick a plan — your API key stays the same: https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
  "payload": {
    "code": "INSUFFICIENT_FUNDS",
    "balance": 0,
    "free_remaining": 0,
    "estimated_cost": 0.002,
    "plans_url": "https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
    "billing_mode": "free_first"
  }
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### AI Places Finder

`POST /scraper/places-ai`

**Price:** `places_ai` $0.005 per call, max $3 per call · `serp_render` $0.002 per call. places_ai + searches × serp_render + tokens × ai_token_markup, capped at meters.places_ai.capUsd. _(list of 2026-10-07)_  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/ai.md)

Describe what you are looking for ("find every car repair shop in Naples"); the model crafts the Google local queries, paginates, de-duplicates and returns the aggregated businesses, optionally enriched from each knowledge panel (phone, website, hours, full address). For a deterministic, per-row-priced alternative use the `google_maps_places` collector. Billed as the `places_ai` base plus `serp_render` per search run plus LLM tokens × ai_token_markup, capped at the `places_ai` meter's capUsd; the pre-check reserves that cap.

##### Request body (required)

`Content-Type: application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `task` | string | yes |  |
| `country` | string | no | ISO country override for the proxy exit and `gl`. |
| `enrich` | boolean | no | Fill phone/website/hours/address from each business's knowledge panel. Default `true`. |
| `max_enrich` | number | no | Knowledge-panel lookups budget (cap 30). Default `15`. |

Example:

```json
{
  "task": "find every car repair shop in Naples",
  "country": "it",
  "max_enrich": 10
}
```

##### Examples

**curl**

```bash
curl -X POST 'https://api.quantumproxies.io/v1/scraper/places-ai' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"task":"find every car repair shop in Naples","country":"it","max_enrich":10}'
```

**Python (requests)**

```python
import requests

r = requests.post(
    'https://api.quantumproxies.io/v1/scraper/places-ai',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    json={
        "task": "find every car repair shop in Naples",
        "country": "it",
        "max_enrich": 10
    },
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/places-ai", {
  method: "POST",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "task": "find every car repair shop in Naples",
    "country": "it",
    "max_enrich": 10
  })
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/places-ai');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY', 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode([
    'task' => 'find every car repair shop in Naples',
    'country' => 'it',
    'max_enrich' => 10
  ]),
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Places found.
- `400` — Malformed input. `message` names the parameter and the rule it broke.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `402` — The free tier plus wallet balance cannot cover the worst-case estimate of this call, or the account's monthly spend cap is reached. Nothing was run or charged.
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Finder failure or not configured on this instance. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Places search successful",
  "payload": {
    "places": [
      {
        "name": "Autofficina Esposito",
        "rating": 4.6,
        "reviews": 212,
        "address": "Via Toledo 12, 80134 Napoli NA",
        "phone": "+39 081 000 0000",
        "website": "https://example.it"
      }
    ],
    "total": 38,
    "enriched": 10,
    "queries": [
      "autofficina Napoli",
      "meccanico Napoli"
    ],
    "summary": "38 repair shops across central Naples.",
    "model": "gpt-4o-mini",
    "usage": {
      "input_tokens": 9800,
      "output_tokens": 2200,
      "cost_usd": 0.0218,
      "free_usd": 0.0218,
      "paid_usd": 0
    }
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Invalid format. Must be one of: markdown, html, text (or data_format markdown/screenshot, format raw)"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `402`:

```json
{
  "type": "error",
  "message": "Your free tier for this month is used up and your balance can't cover this call (estimated $0.002). Add pay-as-you-go credit or pick a plan — your API key stays the same: https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
  "payload": {
    "code": "INSUFFICIENT_FUNDS",
    "balance": 0,
    "free_remaining": 0,
    "estimated_cost": 0.002,
    "plans_url": "https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
    "billing_mode": "free_first"
  }
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### AI Shopping Finder

`POST /scraper/shopping-ai`

**Price:** `shopping_ai` $0.005 per call, max $3 per call · `serp_render` $0.002 per call. shopping_ai + searches × serp_render + tokens × ai_token_markup, capped at meters.shopping_ai.capUsd. _(list of 2026-10-07)_  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/ai.md)

Describe the product ("find the cheapest Nintendo Switch OLED in Italy"); the model crafts Google Shopping queries, paginates, de-duplicates and returns the aggregated products with merchant and price. For a deterministic alternative use /scraper/serp with `search_type: shopping` or the `google_shopping` collector. Billed as the `shopping_ai` base plus `serp_render` per search plus LLM tokens × ai_token_markup, capped at the `shopping_ai` meter's capUsd; the pre-check reserves that cap.

##### Request body (required)

`Content-Type: application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `task` | string | yes |  |
| `country` | string | no | ISO country override for the proxy exit, `gl` and currency. |

Example:

```json
{
  "task": "cheapest Nintendo Switch OLED",
  "country": "it"
}
```

##### Examples

**curl**

```bash
curl -X POST 'https://api.quantumproxies.io/v1/scraper/shopping-ai' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"task":"cheapest Nintendo Switch OLED","country":"it"}'
```

**Python (requests)**

```python
import requests

r = requests.post(
    'https://api.quantumproxies.io/v1/scraper/shopping-ai',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    json={
        "task": "cheapest Nintendo Switch OLED",
        "country": "it"
    },
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/shopping-ai", {
  method: "POST",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "task": "cheapest Nintendo Switch OLED",
    "country": "it"
  })
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/shopping-ai');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY', 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode([
    'task' => 'cheapest Nintendo Switch OLED',
    'country' => 'it'
  ]),
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Products found.
- `400` — Malformed input. `message` names the parameter and the rule it broke.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `402` — The free tier plus wallet balance cannot cover the worst-case estimate of this call, or the account's monthly spend cap is reached. Nothing was run or charged.
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Finder failure or not configured on this instance. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Shopping search successful",
  "payload": {
    "products": [
      {
        "title": "Nintendo Switch OLED Bianco",
        "price": "€ 299,00",
        "merchant": "Example Store",
        "link": "https://example.it/p/switch-oled"
      }
    ],
    "total": 24,
    "queries": [
      "Nintendo Switch OLED prezzo"
    ],
    "searches": [
      {
        "query": "Nintendo Switch OLED prezzo",
        "results": 24
      }
    ],
    "summary": "Lowest price €299 at Example Store.",
    "model": "gpt-4o-mini",
    "usage": {
      "input_tokens": 7100,
      "output_tokens": 900,
      "cost_usd": 0.0121,
      "free_usd": 0.0121,
      "paid_usd": 0
    }
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Invalid format. Must be one of: markdown, html, text (or data_format markdown/screenshot, format raw)"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `402`:

```json
{
  "type": "error",
  "message": "Your free tier for this month is used up and your balance can't cover this call (estimated $0.002). Add pay-as-you-go credit or pick a plan — your API key stays the same: https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
  "payload": {
    "code": "INSUFFICIENT_FUNDS",
    "balance": 0,
    "free_remaining": 0,
    "estimated_cost": 0.002,
    "plans_url": "https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
    "billing_mode": "free_first"
  }
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

### AI visibility

Can AI assistants read and cite a page?

#### AI visibility audit

`POST /scraper/ai-visibility`

**Price:** `ai_visibility` $0.002 per call · `ai_visibility_citation` $0.01 per call · `serp` $0.0005 per call. ai_visibility + answered (query × engine) × ai_visibility_citation + (2 retrieval + 5 offsite) × serp, only for SERPs that answered. Pre-check reserves the worst case. _(list of 2026-10-07)_  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/ai.md)

Answers one question about a page — can an AI assistant read it, and does it cite it? — on two levels. On-page (always): AI crawler access from the real robots.txt (24 bots), Content-Signal, a fetch that identifies itself as an AI crawler, noindex/nosnippet/noai directives, text without JavaScript, structured data and resolvable entities, citable form (questions, opening answer, lists/tables, numbers, chunk-sized sections), dated and authored content — scored per pillar with blockers that cap the total and the evidence behind every check. Citations (only when `queries` is given): the questions are really asked to Perplexity, ChatGPT, Claude, Google AI Overview, Bing Copilot and a DeepSeek-over-Google-SERP engine, and the answer reports who is cited or at least mentioned, plus share of voice against `competitors`. It does not change the page and the citation panel is capped at 10 queries per call and a daily per-account cap on LLM engine calls. Billed as the on-page audit fee plus one `ai_visibility_citation` per (query × engine) that actually answered, plus `serp` for the 2 retrievability SERPs and the 5 offsite SERPs when run.

##### Request body (required)

`Content-Type: application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `url` | string | yes |  |
| `queries` | array of string | no | Questions to ask the engines. Absent = on-page audit only. |
| `engines` | array of string: `perplexity`, `openai`, `anthropic`, `aio`, `copilot`, `deepseek` | no | Subset of engines for the citation panel; default all six. `deepseek` = our Google top-10 handed to DeepSeek (cheapest). |
| `competitors` | array of string | no | Domains to name explicitly in the share of voice. |
| `brand` | string | no | Name to look for in answer text ("mentioned"). |
| `country` | string | no | Country code or place name: letters, digits, spaces, dots, hyphens, underscores; max 56 characters. `all` means no targeting. |
| `no_render` | boolean | no | Skip the rendered pass (cheaper). Alias `noRender`. Default `false`. |
| `no_bot_fetch` | boolean | no | Skip the extra request that identifies as an AI crawler. Alias `noBotFetch`. Default `false`. |
| `no_retrieval` | boolean | no | Skip the 2 retrievability SERPs (rank for the page's own H1 question, index status). Alias `noRetrieval`. Default `false`. |
| `offsite` | boolean | no | Also search the brand on YouTube, Reddit, Wikipedia, LinkedIn and review sites (5 SERPs). Default `false`. |

Example:

```json
{
  "url": "https://example.com/blog/how-to-choose-a-proxy",
  "queries": [
    "how do I choose a residential proxy provider?"
  ],
  "engines": [
    "perplexity",
    "aio"
  ],
  "brand": "Example"
}
```

##### Examples

**curl**

```bash
curl -X POST 'https://api.quantumproxies.io/v1/scraper/ai-visibility' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://example.com/blog/how-to-choose-a-proxy","queries":["how do I choose a residential proxy provider?"],"engines":["perplexity","aio"],"brand":"Example"}'
```

**Python (requests)**

```python
import requests

r = requests.post(
    'https://api.quantumproxies.io/v1/scraper/ai-visibility',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    json={
        "url": "https://example.com/blog/how-to-choose-a-proxy",
        "queries": [
            "how do I choose a residential proxy provider?"
        ],
        "engines": [
            "perplexity",
            "aio"
        ],
        "brand": "Example"
    },
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/ai-visibility", {
  method: "POST",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "url": "https://example.com/blog/how-to-choose-a-proxy",
    "queries": [
      "how do I choose a residential proxy provider?"
    ],
    "engines": [
      "perplexity",
      "aio"
    ],
    "brand": "Example"
  })
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/ai-visibility');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY', 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode([
    'url' => 'https://example.com/blog/how-to-choose-a-proxy',
    'queries' => ['how do I choose a residential proxy provider?'],
    'engines' => ['perplexity', 'aio'],
    'brand' => 'Example'
  ]),
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Audit finished. `billing` says how many citation calls and SERPs were charged.
- `400` — Malformed input. `message` names the parameter and the rule it broke.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `402` — The free tier plus wallet balance cannot cover the worst-case estimate of this call, or the account's monthly spend cap is reached. Nothing was run or charged.
- `403` — No proxy pool is available to serve the call (no house plan and no residential/datacenter/IPv6 plan on the account).
- `429` — Key/plan rate limit, or the account's daily cap on external AI-engine calls (payload: limit, remaining, reset_at, requested).
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "AI visibility audit successful",
  "payload": {
    "url": "https://example.com/blog/how-to-choose-a-proxy",
    "finalUrl": "https://example.com/blog/how-to-choose-a-proxy",
    "domain": "example.com",
    "score": {
      "overall": 71,
      "pillars": {
        "access": 90,
        "content": 68,
        "structure": 60,
        "citability": 66
      },
      "scoredPillars": [
        "access",
        "content",
        "structure",
        "citability"
      ],
      "blockers": [],
      "uncapped": 71
    },
    "checks": [
      {
        "id": "robots_gptbot",
        "pillar": "access",
        "status": "pass",
        "weight": 8,
        "title": "GPTBot allowed",
        "detail": "robots.txt does not disallow GPTBot.",
        "evidence": {
          "rule": null
        }
      }
    ],
    "topFixes": [
      {
        "id": "author_resolvable",
        "status": "fail",
        "title": "Author not resolvable",
        "fix": "Add an author Person node with url or sameAs."
      }
    ],
    "access": {
      "robotsTxtFound": true,
      "robotsTxtUrl": "https://example.com/robots.txt",
      "blockedCritical": [],
      "sitemaps": [
        "https://example.com/sitemap.xml"
      ],
      "llmsTxt": false
    },
    "content": {
      "contentOnlyInJs": false,
      "fkGrade": 9.1
    },
    "structure": {
      "jsonldTypes": [
        "Article"
      ],
      "hasAuthor": true,
      "authorResolvable": false
    },
    "citations": {
      "rows": [
        {
          "query": "how do I choose a residential proxy provider?",
          "engine": "perplexity",
          "cited": false,
          "mentioned": true,
          "rank": null,
          "citedDomains": [
            "competitor.example"
          ]
        }
      ],
      "usage": {
        "perplexity": {
          "searches": 1
        }
      }
    },
    "geo": {
      "country": null
    },
    "billing": {
      "citation_calls_billed": 2,
      "offsite_serps_billed": 2
    },
    "usage": {
      "cost_usd": 0.023,
      "free_usd": 0.023,
      "paid_usd": 0
    }
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Invalid format. Must be one of: markdown, html, text (or data_format markdown/screenshot, format raw)"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `402`:

```json
{
  "type": "error",
  "message": "Your free tier for this month is used up and your balance can't cover this call (estimated $0.002). Add pay-as-you-go credit or pick a plan — your API key stays the same: https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
  "payload": {
    "code": "INSUFFICIENT_FUNDS",
    "balance": 0,
    "free_remaining": 0,
    "estimated_cost": 0.002,
    "plans_url": "https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
    "billing_mode": "free_first"
  }
}
```

Example `403`:

```json
{
  "type": "error",
  "message": "Scraper API is not available: no proxy credentials found. Contact support or purchase a residential, datacenter or IPv6 plan."
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

### SEO audit

No-JS vs rendered view of a page and the diff between them.

#### No-JS vs rendered SEO audit

`POST /scraper/seo-audit`

**Price:** `seo_audit` $0.0012 per call · `extract` $0.0002 per call + $3/GB, max $0.02 per call. seo_audit when the render pass ran; extract when no_render is true or the render pass errored. _(list of 2026-10-07)_  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
**Short alias:** `POST https://api.quantumproxies.io/v1/seo-audit` (same handler, primary host only)  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/ai.md)

Fetches a URL twice — once as a pure HTTP bot (no JavaScript) and once fully rendered — and returns what search engines see in each view, the diff (title/description changes, H1 or content only after JS, missing canonical) and the bot-facing meta (robots, OpenGraph, JSON-LD types). It is a page-level technical check, not a site crawl or a keyword tool. The full audit takes one browser-render token; with `no_render` only the no-JS view is produced and the call bills the plain scrape price. If the render pass fails on our side you still get the no-JS view at the plain scrape price.

##### Request body (required)

`Content-Type: application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `url` | string | yes |  |
| `country` | string | no | Country code or place name: letters, digits, spaces, dots, hyphens, underscores; max 56 characters. `all` means no targeting. |
| `no_render` | boolean | no | Skip the rendered pass. Alias `noRender`. Default `false`. |

Example:

```json
{
  "url": "https://example.com"
}
```

##### Examples

**curl**

```bash
curl -X POST 'https://api.quantumproxies.io/v1/scraper/seo-audit' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://example.com"}'
```

**Python (requests)**

```python
import requests

r = requests.post(
    'https://api.quantumproxies.io/v1/scraper/seo-audit',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    json={
        "url": "https://example.com"
    },
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/seo-audit", {
  method: "POST",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "url": "https://example.com"
  })
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/seo-audit');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY', 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode([
    'url' => 'https://example.com'
  ]),
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Audit finished.
- `400` — Malformed input. `message` names the parameter and the rule it broke.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `402` — The free tier plus wallet balance cannot cover the worst-case estimate of this call, or the account's monthly spend cap is reached. Nothing was run or charged.
- `403` — No proxy pool is available to serve the call (no house plan and no residential/datacenter/IPv6 plan on the account).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "SEO audit successful",
  "payload": {
    "url": "https://example.com",
    "finalUrl": "https://example.com/",
    "noJs": {
      "status": 200,
      "title": "Example Domain",
      "description": null,
      "canonical": null,
      "h1": "Example Domain",
      "wordCount": 28,
      "hasContent": false
    },
    "render": {
      "status": 200,
      "title": "Example Domain",
      "description": null,
      "canonical": null,
      "h1": "Example Domain",
      "wordCount": 28,
      "hasContent": false
    },
    "diff": {
      "titleChanged": false,
      "descriptionChanged": false,
      "h1OnlyInRender": false,
      "canonicalMissingNoJs": true,
      "contentOnlyInJs": false
    },
    "meta": {
      "robots": null,
      "ogTitle": null,
      "ogUrl": null,
      "twitterCard": null,
      "jsonldTypes": []
    },
    "durationMs": 4210,
    "geo": {
      "country": null
    },
    "usage": {
      "cost_usd": 0.0012,
      "free_usd": 0.0012,
      "paid_usd": 0
    }
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Invalid format. Must be one of: markdown, html, text (or data_format markdown/screenshot, format raw)"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `402`:

```json
{
  "type": "error",
  "message": "Your free tier for this month is used up and your balance can't cover this call (estimated $0.002). Add pay-as-you-go credit or pick a plan — your API key stays the same: https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
  "payload": {
    "code": "INSUFFICIENT_FUNDS",
    "balance": 0,
    "free_remaining": 0,
    "estimated_cost": 0.002,
    "plans_url": "https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
    "billing_mode": "free_first"
  }
}
```

Example `403`:

```json
{
  "type": "error",
  "message": "Scraper API is not available: no proxy credentials found. Contact support or purchase a residential, datacenter or IPv6 plan."
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

## Collectors & datasets

Markdown for this group only: https://quantumproxies.io/docs/collectors-datasets.md

### Collectors

Ready-made, versioned scrapers on a semantic input; billed per delivered row. The catalog is dynamic: read it from GET /scraper/collectors.

#### Collector catalog

`GET /scraper/collectors`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/collectors-datasets.md)

Every ready-made collector with its identity and version, semantic `input_schema` (JSON Schema — what to POST to `run_url`), full `output_schema`, examples, hourly health probe and YOUR price per delivered result (list price × tier discount). The catalog is the source of truth: collectors are added and versioned without an API change, so read it rather than hard-coding slugs. Free.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `category` | query | string: `search`, `seo`, `social`, `apps`, `real_estate`, `local`, `jobs`, `news`, `ecommerce`, `travel`, `leads`, `company`, `classifieds`, `finance`, `dev`, `knowledge`, `gaming`, `osint`, `research` | no | Only collectors of this category. |

##### Examples

**curl**

```bash
curl -X GET 'https://api.quantumproxies.io/v1/scraper/collectors' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.get(
    'https://api.quantumproxies.io/v1/scraper/collectors',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/collectors", {
  method: "GET",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/collectors');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Catalog.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Collectors",
  "payload": {
    "collectors": [
      {
        "slug": "google_maps_places",
        "name": "Google Maps places",
        "version": "1.1.0",
        "category": "local",
        "category_label": "Local & Maps",
        "tagline": "Businesses for a keyword in a location — name, rating, address, phone, website, coordinates.",
        "description": "Searches Google's local results for a keyword in a location and delivers de-duplicated place records with contact and geo fields.",
        "unit": "place",
        "engines": [
          "serp"
        ],
        "price": {
          "list_usd": 0.001,
          "your_usd": 0.001,
          "price_key": "collector_google_maps_places",
          "per_1k_usd": 1,
          "min_billable_results": 0,
          "min_run_usd": 0
        },
        "max_results": 300,
        "input_schema": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string",
              "title": "Keyword",
              "minLength": 2,
              "maxLength": 120,
              "examples": [
                "pizza restaurants"
              ]
            },
            "location": {
              "type": "string",
              "title": "Location",
              "minLength": 2,
              "maxLength": 120,
              "examples": [
                "Brooklyn, NY"
              ]
            },
            "country": {
              "type": "string",
              "format": "country"
            },
            "lang": {
              "type": "string",
              "format": "lang"
            },
            "max_results": {
              "type": "integer",
              "default": 20,
              "minimum": 1,
              "maximum": 300
            },
            "enrich_details": {
              "type": "boolean",
              "default": true
            }
          },
          "required": [
            "query",
            "location"
          ],
          "additionalProperties": false
        },
        "output_schema": {
          "fields": [
            {
              "name": "rank",
              "type": "integer",
              "description": "1-based position across the merged pages."
            },
            {
              "name": "name",
              "type": "string",
              "description": "Business name."
            },
            {
              "name": "rating",
              "type": "number",
              "nullable": true,
              "description": "Star rating (1–5)."
            },
            {
              "name": "place_id",
              "type": "string",
              "nullable": true,
              "description": "Google place id."
            }
          ]
        },
        "examples": [
          {
            "title": "Pizza in Brooklyn",
            "input": {
              "query": "pizza restaurants",
              "location": "Brooklyn, NY",
              "country": "us",
              "max_results": 20
            }
          }
        ],
        "health_input": {
          "query": "pizza",
          "location": "Brooklyn, NY",
          "country": "us",
          "max_results": 10
        },
        "health": {
          "status": "healthy",
          "checked_at": "2026-10-07T08:00:12.000Z",
          "latency_ms": 6210,
          "result_count": 10,
          "error": null,
          "success_rate_24h": 1,
          "last_ok_at": "2026-10-07T08:00:12.000Z"
        },
        "run_url": "/api/v1/scraper/collectors/google_maps_places/run"
      }
    ],
    "billing": {
      "enabled": true,
      "discount_multiplier": 1
    }
  },
  "pagination": {}
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### One collector

`GET /scraper/collectors/{slug}`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/collectors-datasets.md)

The catalog entry of one collector plus its changelog and the last 24 health probes. Free.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `slug` | path | string | yes | Collector slug from the catalog. |

##### Examples

**curl**

```bash
curl -X GET 'https://api.quantumproxies.io/v1/scraper/collectors/google_maps_places' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.get(
    'https://api.quantumproxies.io/v1/scraper/collectors/google_maps_places',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/collectors/google_maps_places", {
  method: "GET",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/collectors/google_maps_places');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Collector.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `404` — Unknown slug.
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Collector",
  "payload": {
    "collector": {
      "slug": "google_maps_places",
      "name": "Google Maps places",
      "version": "1.1.0",
      "category": "local",
      "category_label": "Local & Maps",
      "tagline": "Businesses for a keyword in a location.",
      "description": "…",
      "unit": "place",
      "engines": [
        "serp"
      ],
      "price": {
        "list_usd": 0.001,
        "your_usd": 0.001,
        "price_key": "collector_google_maps_places",
        "per_1k_usd": 1,
        "min_billable_results": 0,
        "min_run_usd": 0
      },
      "max_results": 300,
      "input_schema": {
        "type": "object",
        "properties": {},
        "required": [
          "query",
          "location"
        ],
        "additionalProperties": false
      },
      "output_schema": {
        "fields": []
      },
      "examples": [],
      "health_input": {},
      "health": {
        "status": "healthy",
        "checked_at": "2026-10-07T08:00:12.000Z",
        "latency_ms": 6210,
        "result_count": 10,
        "error": null,
        "success_rate_24h": 1,
        "last_ok_at": "2026-10-07T08:00:12.000Z"
      },
      "run_url": "/api/v1/scraper/collectors/google_maps_places/run",
      "changelog": [
        {
          "version": "1.1.0",
          "date": "2026-08-21",
          "notes": "Paginates past offset 240."
        }
      ],
      "health_history": [
        {
          "checked_at": "2026-10-07T08:00:12.000Z",
          "ok": true,
          "latency_ms": 6210,
          "result_count": 10,
          "error": null
        }
      ]
    }
  },
  "pagination": {}
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `404`:

```json
{
  "type": "error",
  "message": "Unknown collector \"foo\""
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Run a collector

`POST /scraper/collectors/{slug}/run`

**Price:** `collector_result` per delivered result, price by collector (see the collector price table). Per delivered row: pricing.json key `collector_<slug>` when it exists, else `collector_result`. GET /scraper/collectors returns the resolved key and your discounted price for every collector. _(list of 2026-10-07)_  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/collectors-datasets.md)

The body is the collector's semantic input exactly as published in its `input_schema` (flat, or wrapped as `{"input": {...}}`), plus runner options: `async: true` forces background processing (also `mode: "async"` or `wait: false`). Short runs answer synchronously with the rows; long ones (the collector decides from the input size) answer 202 with a `statusUrl` to poll. Identical input re-sent inside the dedup window returns the existing run (`deduplicated: true`) — nothing new is created or charged. Per-account brakes (runs per hour, concurrent runs, failure streak) answer 429 with Retry-After. Billed per DELIVERED row at the collector's unit price × your tier discount (some collectors have a small per-run floor, see `price.min_billable_results`); zero rows = zero charge, failed runs are never billed. The worst case (unit × max_results) must be covered by free tier + balance or the run is refused with 402.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `slug` | path | string | yes | Collector slug from the catalog. |

##### Request body (required)

`Content-Type: application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `async` | boolean | no | Force background processing (202 + statusUrl). Default `false`. |
| `mode` | string: `async` | no | Alias of `async: true`. |
| `wait` | boolean | no | `false` = alias of `async: true`. |
| `input` | object | no | Optional wrapper for the collector input. |

Example:

```json
{
  "query": "pizza restaurants",
  "location": "Brooklyn, NY",
  "country": "us",
  "max_results": 20
}
```

##### Examples

**curl**

```bash
curl -X POST 'https://api.quantumproxies.io/v1/scraper/collectors/google_maps_places/run' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"query":"pizza restaurants","location":"Brooklyn, NY","country":"us","max_results":20}'
```

**Python (requests)**

```python
import requests

r = requests.post(
    'https://api.quantumproxies.io/v1/scraper/collectors/google_maps_places/run',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    json={
        "query": "pizza restaurants",
        "location": "Brooklyn, NY",
        "country": "us",
        "max_results": 20
    },
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/collectors/google_maps_places/run", {
  method: "POST",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "query": "pizza restaurants",
    "location": "Brooklyn, NY",
    "country": "us",
    "max_results": 20
  })
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/collectors/google_maps_places/run');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY', 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode([
    'query' => 'pizza restaurants',
    'location' => 'Brooklyn, NY',
    'country' => 'us',
    'max_results' => 20
  ]),
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Synchronous run finished with rows.
- `202` — Run queued (or an identical run already in progress): poll `statusUrl`.
- `400` — Input does not match the collector's `input_schema` (`payload.errors`).
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `402` — The free tier plus wallet balance cannot cover the worst-case estimate of this call, or the account's monthly spend cap is reached. Nothing was run or charged.
- `404` — Unknown slug.
- `422` — Input that can never resolve (documentation placeholder, undelegated TLD) — before or after the run; retrying the same input cannot help. Not billed.
- `424` — The run failed at the source (blocked, timed out, layout changed). JSON body with `run_id` and `error`; not billed, safe to retry.
- `429` — Key/plan rate limit, or a per-account collector brake (`payload.code`: RUNS_PER_HOUR, CONCURRENT_RUNS, FAIL_STREAK, USER_PAUSED) with Retry-After.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Collector run complete",
  "payload": {
    "run_id": "cmgfq2x1b0001",
    "slug": "google_maps_places",
    "version": "1.1.0",
    "status": "done",
    "input": {
      "query": "pizza restaurants",
      "location": "Brooklyn, NY",
      "country": "us",
      "max_results": 20
    },
    "count": 20,
    "partial": false,
    "cost": {
      "usd": 0.02,
      "unit_usd": 0.001,
      "unit": "place",
      "billed": true
    },
    "error": null,
    "results": [
      {
        "rank": 1,
        "name": "Joe's Pizza",
        "rating": 4.5,
        "reviews": 3120,
        "category": "Pizza restaurant",
        "address": "7 Carmine St, New York, NY 10014",
        "phone": "+1 212-366-1182",
        "website": "joespizzanyc.com",
        "latitude": 40.7306,
        "longitude": -74.0027,
        "place_id": "ChIJ…",
        "data_id": "0x89c259…:0x…",
        "found_by": "pizza restaurants Brooklyn, NY"
      }
    ],
    "notes": [],
    "created_at": "2026-10-07T09:20:01.000Z",
    "started_at": "2026-10-07T09:20:01.000Z",
    "finished_at": "2026-10-07T09:20:14.000Z",
    "usage": {
      "cost_usd": 0.02,
      "free_usd": 0.02,
      "paid_usd": 0
    }
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Invalid input: location is required",
  "payload": {
    "errors": [
      "location is required"
    ]
  }
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `402`:

```json
{
  "type": "error",
  "message": "Your free tier for this month is used up and your balance can't cover this call (estimated $0.002). Add pay-as-you-go credit or pick a plan — your API key stays the same: https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
  "payload": {
    "code": "INSUFFICIENT_FUNDS",
    "balance": 0,
    "free_remaining": 0,
    "estimated_cost": 0.002,
    "plans_url": "https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
    "billing_mode": "free_first"
  }
}
```

Example `422`:

```json
{
  "type": "error",
  "message": "Invalid input: example.com is a documentation placeholder",
  "payload": {
    "errors": [
      "example.com is a documentation placeholder"
    ],
    "error_kind": "input"
  }
}
```

Example `424`:

```json
{
  "type": "error",
  "message": "Collector run failed",
  "payload": {
    "run_id": "cmgfq2x1b0002",
    "slug": "google_maps_places",
    "version": "1.1.0",
    "status": "failed",
    "input": {},
    "count": 0,
    "partial": false,
    "cost": {
      "usd": 0,
      "unit_usd": 0.001,
      "unit": "place",
      "billed": false
    },
    "error": "source timed out after 3 attempts",
    "created_at": "2026-10-07T09:20:01.000Z",
    "started_at": "2026-10-07T09:20:01.000Z",
    "finished_at": "2026-10-07T09:21:01.000Z"
  }
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Too many collector runs this hour.",
  "payload": {
    "code": "RUNS_PER_HOUR",
    "limit": 60,
    "in_last_hour": 60,
    "retry_after": 900,
    "plan": "payg"
  }
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Your collector runs

`GET /scraper/collectors/runs`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/collectors-datasets.md)

Newest first, without result rows. Page with `next_cursor`. Free.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `limit` | query | integer | no |  Default `20`. |
| `cursor` | query | string | no | `next_cursor` from the previous page. |
| `slug` | query | string | no | Only runs of this collector. |

##### Examples

**curl**

```bash
curl -X GET 'https://api.quantumproxies.io/v1/scraper/collectors/runs' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.get(
    'https://api.quantumproxies.io/v1/scraper/collectors/runs',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/collectors/runs", {
  method: "GET",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/collectors/runs');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Runs.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Collector runs",
  "payload": {
    "runs": [
      {
        "run_id": "cmgfq2x1b0001",
        "slug": "google_maps_places",
        "version": "1.1.0",
        "status": "done",
        "input": {
          "query": "pizza restaurants",
          "location": "Brooklyn, NY"
        },
        "count": 20,
        "partial": false,
        "cost": {
          "usd": 0.02,
          "unit_usd": 0.001,
          "unit": "place",
          "billed": true
        },
        "error": null,
        "created_at": "2026-10-07T09:20:01.000Z",
        "started_at": "2026-10-07T09:20:01.000Z",
        "finished_at": "2026-10-07T09:20:14.000Z"
      }
    ],
    "next_cursor": null
  },
  "pagination": {}
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### One run with its rows

`GET /scraper/collectors/runs/{runId}`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/collectors-datasets.md)

The run you own with its delivered results. `format=csv` streams the rows as CSV (columns follow the collector's `output_schema`). Free.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `runId` | path | string | yes | Collector run id (10–40 alphanumeric characters). |
| `format` | query | string: `csv` | no | `csv` returns a file instead of the JSON envelope. |

##### Examples

**curl**

```bash
curl -X GET 'https://api.quantumproxies.io/v1/scraper/collectors/runs/cmgfq2x1b0001' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.get(
    'https://api.quantumproxies.io/v1/scraper/collectors/runs/cmgfq2x1b0001',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/collectors/runs/cmgfq2x1b0001", {
  method: "GET",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/collectors/runs/cmgfq2x1b0001');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Run view (JSON) or CSV file.
- `400` — Malformed run id.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `404` — Unknown id — or an id that belongs to another account (ownership failures answer 404, never 403, so ids cannot be probed).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Collector run",
  "payload": {
    "run_id": "cmgfq2x1b0001",
    "slug": "google_maps_places",
    "version": "1.1.0",
    "status": "done",
    "input": {
      "query": "pizza restaurants",
      "location": "Brooklyn, NY",
      "country": "us",
      "max_results": 20
    },
    "count": 20,
    "partial": false,
    "cost": {
      "usd": 0.02,
      "unit_usd": 0.001,
      "unit": "place",
      "billed": true
    },
    "error": null,
    "results": [
      {
        "rank": 1,
        "name": "Joe's Pizza"
      }
    ],
    "created_at": "2026-10-07T09:20:01.000Z",
    "started_at": "2026-10-07T09:20:01.000Z",
    "finished_at": "2026-10-07T09:20:14.000Z",
    "usage": {
      "cost_usd": 0.02,
      "free_usd": 0.02,
      "paid_usd": 0
    }
  },
  "pagination": {}
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `404`:

```json
{
  "type": "error",
  "message": "Job not found"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

### Datasets

Prompt-driven dataset builder: one prompt, a validated table.

#### Your recent dataset runs

`GET /scraper/datasets`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/collectors-datasets.md)

The last 20 runs, newest first, with status reconciled against the live job for runs still marked running, the settled cost, and whether the rows are still stored. Free.

##### Examples

**curl**

```bash
curl -X GET 'https://api.quantumproxies.io/v1/scraper/datasets' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.get(
    'https://api.quantumproxies.io/v1/scraper/datasets',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/datasets", {
  method: "GET",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/datasets');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Datasets.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Datasets",
  "payload": {
    "datasets": [
      {
        "id": "cmgf0",
        "jobId": "ds_7a1c22",
        "name": null,
        "prompt": "Car rental companies in Bologna with phone and website",
        "columns": "[{\"name\":\"company\",\"type\":\"string\"},{\"name\":\"phone\",\"type\":\"phone\"},{\"name\":\"website\",\"type\":\"url\"}]",
        "country": "it",
        "status": "completed",
        "rowCount": 42,
        "billableRows": 40,
        "refresh": false,
        "createdAt": "2026-10-06T14:02:11.000Z",
        "completedAt": "2026-10-06T14:09:40.000Z",
        "purgedAt": null,
        "costUsd": 1.86,
        "stored": true,
        "storedRows": 42,
        "storedBytes": 18311,
        "purged": false,
        "dropped": null
      }
    ]
  },
  "pagination": {}
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Build a dataset from a prompt

`POST /scraper/datasets`

**Price:** `dataset_row` $0.03 per call · `dataset_row_refresh` $0.01 per call. max_cost_usd charged up front, refunded down to actual spend. Actual spend = delivered validated rows × dataset_row (dataset_row_refresh on refresh) + premium field fees + metered pipeline units (serp/map/extract/AI). _(list of 2026-10-07)_  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/collectors-datasets.md)

One plain-language prompt → a structured dataset. The service plans queries, searches, maps and scrapes, extracts the requested columns and returns validated rows; `refresh: true` re-reads a known URL set (`refreshUrls`) without discovery at a reduced row fee. Returns a job id immediately; poll `GET /scraper/datasets/{jobId}`, download the file, or pass a `webhook` (public host only, delivered after settlement with the hygiene filters applied). Billing is value-based: the run's budget `limits.max_cost_usd` (default $5, max $500, min $0.05) is charged up front and the unspent share is refunded at settlement, so you pay only for delivered, validated records (`dataset_row`, or `dataset_row_refresh` on a refresh) plus premium columns found (email, phone, deep) plus the pipeline units consumed. Low-confidence rows, off-target pages and blocked pages are never billed.

##### Request body (required)

`Content-Type: application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `prompt` | string | yes | What the dataset is. |
| `columns` | array of object | no | Omit to let the planner infer columns from the prompt. |
| `columns[].name` | string | yes |  |
| `columns[].type` | string: `string`, `number`, `email`, `phone`, `url`, `boolean`, `deep` | no | `email`, `phone` and `deep` are premium (billed only when found). |
| `columns[].description` | string | no |  |
| `country` | string | no | Country code or place name: letters, digits, spaces, dots, hyphens, underscores; max 56 characters. `all` means no targeting. |
| `sources` | object | no | Domain allow/deny lists. |
| `sources.include` | array of string | no |  |
| `sources.exclude` | array of string | no |  |
| `limits` | object | no |  |
| `limits.max_rows` | integer | no |  |
| `limits.max_pages` | integer | no |  |
| `limits.max_cost_usd` | number | no |  Default `5`. |
| `webhook` | string | no | Public http(s) URL that receives the finished, filtered dataset by POST. |
| `refresh` | boolean | no |  Default `false`. |
| `refreshUrls` | array of string | no | Required with `refresh: true`. |

Example:

```json
{
  "prompt": "Car rental companies in Bologna with phone and website",
  "columns": [
    {
      "name": "company",
      "type": "string"
    },
    {
      "name": "phone",
      "type": "phone"
    },
    {
      "name": "website",
      "type": "url"
    }
  ],
  "country": "it",
  "limits": {
    "max_rows": 50,
    "max_cost_usd": 3
  }
}
```

##### Examples

**curl**

```bash
curl -X POST 'https://api.quantumproxies.io/v1/scraper/datasets' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"prompt":"Car rental companies in Bologna with phone and website","columns":[{"name":"company","type":"string"},{"name":"phone","type":"phone"},{"name":"website","type":"url"}],"country":"it","limits":{"max_rows":50,"max_cost_usd":3}}'
```

**Python (requests)**

```python
import requests

r = requests.post(
    'https://api.quantumproxies.io/v1/scraper/datasets',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    json={
        "prompt": "Car rental companies in Bologna with phone and website",
        "columns": [
            {
                "name": "company",
                "type": "string"
            },
            {
                "name": "phone",
                "type": "phone"
            },
            {
                "name": "website",
                "type": "url"
            }
        ],
        "country": "it",
        "limits": {
            "max_rows": 50,
            "max_cost_usd": 3
        }
    },
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/datasets", {
  method: "POST",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "prompt": "Car rental companies in Bologna with phone and website",
    "columns": [
      {
        "name": "company",
        "type": "string"
      },
      {
        "name": "phone",
        "type": "phone"
      },
      {
        "name": "website",
        "type": "url"
      }
    ],
    "country": "it",
    "limits": {
      "max_rows": 50,
      "max_cost_usd": 3
    }
  })
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/datasets');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY', 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode([
    'prompt' => 'Car rental companies in Bologna with phone and website',
    'columns' => [[
      'name' => 'company',
      'type' => 'string'
    ], [
      'name' => 'phone',
      'type' => 'phone'
    ], [
      'name' => 'website',
      'type' => 'url'
    ]],
    'country' => 'it',
    'limits' => [
      'max_rows' => 50,
      'max_cost_usd' => 3
    ]
  ]),
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Dataset job started.
- `400` — Malformed input. `message` names the parameter and the rule it broke.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `402` — The free tier plus wallet balance cannot cover the worst-case estimate of this call, or the account's monthly spend cap is reached. Nothing was run or charged.
- `403` — No proxy pool is available to serve the call (no house plan and no residential/datacenter/IPv6 plan on the account).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — The Data API group is off for your key, or the dataset builder is not enabled on this instance (configuration, not a transient error).

Example `200` response:

```json
{
  "type": "response",
  "message": "Dataset started",
  "payload": {
    "id": "ds_7a1c22",
    "status": "running",
    "prompt": "Car rental companies in Bologna with phone and website",
    "columns": [
      {
        "name": "company",
        "type": "string"
      },
      {
        "name": "phone",
        "type": "phone"
      },
      {
        "name": "website",
        "type": "url"
      }
    ],
    "limits": {
      "max_rows": 50,
      "max_pages": 200,
      "max_cost_usd": 3
    },
    "statusUrl": "/api/v1/scraper/datasets/ds_7a1c22",
    "usage": {
      "cost_usd": 3,
      "free_usd": 2,
      "paid_usd": 1,
      "balance": 9
    }
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Invalid format. Must be one of: markdown, html, text (or data_format markdown/screenshot, format raw)"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `402`:

```json
{
  "type": "error",
  "message": "Your free tier for this month is used up and your balance can't cover this call (estimated $0.002). Add pay-as-you-go credit or pick a plan — your API key stays the same: https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
  "payload": {
    "code": "INSUFFICIENT_FUNDS",
    "balance": 0,
    "free_remaining": 0,
    "estimated_cost": 0.002,
    "plans_url": "https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
    "billing_mode": "free_first"
  }
}
```

Example `403`:

```json
{
  "type": "error",
  "message": "Scraper API is not available: no proxy credentials found. Contact support or purchase a residential, datacenter or IPv6 plan."
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Poll a dataset job

`GET /scraper/datasets/{jobId}`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/collectors-datasets.md)

Progress, the collection trace (`steps`) and the rows so far, filtered for you (removal list, your exclusion list, rows already delivered to you). A run the service has already forgotten (about an hour after it finishes) is served from storage (`source: storage`, no progress/steps). Free.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `jobId` | path | string | yes | Job id returned by the POST that started it. |
| `since` | query | integer | no | Row cursor from the previous poll's `nextCursor`. |
| `mode` | query | string: `summary` | no | `summary` omits rows. |

##### Examples

**curl**

```bash
curl -X GET 'https://api.quantumproxies.io/v1/scraper/datasets/job_8f2c1a' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.get(
    'https://api.quantumproxies.io/v1/scraper/datasets/job_8f2c1a',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/datasets/job_8f2c1a", {
  method: "GET",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/datasets/job_8f2c1a');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Job view.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `404` — Unknown id — or an id that belongs to another account (ownership failures answer 404, never 403, so ids cannot be probed).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Dataset status",
  "payload": {
    "id": "ds_7a1c22",
    "status": "completed",
    "prompt": "Car rental companies in Bologna with phone and website",
    "columns": [
      {
        "name": "company",
        "type": "string"
      },
      {
        "name": "phone",
        "type": "phone"
      },
      {
        "name": "website",
        "type": "url"
      }
    ],
    "files": null,
    "progress": {
      "queries_run": 6,
      "sites_mapped": 12,
      "pages_scraped": 58,
      "pages_failed": 3,
      "rows": 42,
      "dropped_offtarget": 4,
      "cost_so_far_usd": 1.86
    },
    "limits": {
      "max_rows": 50,
      "max_pages": 200,
      "max_cost_usd": 3
    },
    "entity": "car rental company",
    "billable": {
      "row_fees_usd": 1.62,
      "unit_usd": 0.24
    },
    "steps": [
      {
        "phase": "plan",
        "detail": "entity: car rental company; 6 queries",
        "ts": 1759759331000
      }
    ],
    "rows": [
      {
        "fields": {
          "company": "Bologna Rent",
          "phone": "+39 051 000000",
          "website": "https://example.it"
        },
        "_source_url": "https://example.it/contatti",
        "_fetched_at": "2026-10-06T14:05:10.000Z",
        "confidence": 0.93,
        "billed_fields": [
          "phone"
        ],
        "fee_usd": 0.05
      }
    ],
    "row_count": 42,
    "billable_rows": 40,
    "nextCursor": 42,
    "createdAt": 1759759331000,
    "finishedAt": 1759759780000
  },
  "pagination": {}
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `404`:

```json
{
  "type": "error",
  "message": "Job not found"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Cancel a dataset job

`DELETE /scraper/datasets/{jobId}`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/collectors-datasets.md)

Stops a running job you own; the unspent budget is refunded at settlement. To delete the stored record of a finished run use `DELETE /scraper/datasets/{jobId}/record`.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `jobId` | path | string | yes | Job id returned by the POST that started it. |

##### Examples

**curl**

```bash
curl -X DELETE 'https://api.quantumproxies.io/v1/scraper/datasets/job_8f2c1a' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.delete(
    'https://api.quantumproxies.io/v1/scraper/datasets/job_8f2c1a',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/datasets/job_8f2c1a", {
  method: "DELETE",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/datasets/job_8f2c1a');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'DELETE',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Cancelled.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `404` — Unknown id — or an id that belongs to another account (ownership failures answer 404, never 403, so ids cannot be probed).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Dataset cancelled",
  "payload": {
    "id": "ds_7a1c22",
    "status": "cancelled"
  },
  "pagination": {}
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `404`:

```json
{
  "type": "error",
  "message": "Job not found"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Download a dataset file

`GET /scraper/datasets/{jobId}/download`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/collectors-datasets.md)

The run's rows as CSV or JSON, partial runs included (rows collected before an interruption are delivered too). Always built from rows that passed the list-hygiene filter. 404 while the run is still going or when it produced no rows. Free.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `jobId` | path | string | yes | Job id returned by the POST that started it. |
| `format` | query | string: `csv`, `json` | no |  Default `"csv"`. |

##### Examples

**curl**

```bash
curl -X GET 'https://api.quantumproxies.io/v1/scraper/datasets/job_8f2c1a/download' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.get(
    'https://api.quantumproxies.io/v1/scraper/datasets/job_8f2c1a/download',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
r.raise_for_status()
open("response.out", "wb").write(r.content)
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/datasets/job_8f2c1a/download", {
  method: "GET",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const bytes = Buffer.from(await res.arrayBuffer());
require("node:fs").writeFileSync("response.out", bytes);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/datasets/job_8f2c1a/download');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
file_put_contents('response.out', $raw);
```

##### Responses

- `200` — File attachment `dataset-{jobId}.csv\|json`.
- `400` — Malformed input. `message` names the parameter and the rule it broke.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `404` — Unknown job, not yours, or file not ready (run not finished or no rows).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "columns": [
    {
      "name": "company",
      "type": "string"
    }
  ],
  "rows": [
    {
      "company": "Bologna Rent",
      "_source_url": "https://example.it/contatti"
    }
  ],
  "rowCount": 1
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Invalid format. Must be one of: markdown, html, text (or data_format markdown/screenshot, format raw)"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `404`:

```json
{
  "type": "error",
  "message": "File not ready — the run has not finished or produced no rows"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Rename a dataset run

`PATCH /scraper/datasets/{jobId}/record`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/collectors-datasets.md)

Sets the run's label (max 120 characters); an empty string or null clears it and the prompt becomes the label again. The prompt itself is never rewritten. Free.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `jobId` | path | string | yes | Job id returned by the POST that started it. |

##### Request body (required)

`Content-Type: application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | no |  |

Example:

```json
{
  "name": "Bologna car rentals — October"
}
```

##### Examples

**curl**

```bash
curl -X PATCH 'https://api.quantumproxies.io/v1/scraper/datasets/job_8f2c1a/record' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bologna car rentals — October"}'
```

**Python (requests)**

```python
import requests

r = requests.patch(
    'https://api.quantumproxies.io/v1/scraper/datasets/job_8f2c1a/record',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    json={
        "name": "Bologna car rentals — October"
    },
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/datasets/job_8f2c1a/record", {
  method: "PATCH",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "name": "Bologna car rentals — October"
  })
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/datasets/job_8f2c1a/record');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'PATCH',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY', 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode([
    'name' => 'Bologna car rentals — October'
  ]),
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Renamed.
- `400` — Malformed input. `message` names the parameter and the rule it broke.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `404` — Unknown id — or an id that belongs to another account (ownership failures answer 404, never 403, so ids cannot be probed).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Dataset renamed",
  "payload": {
    "jobId": "ds_7a1c22",
    "name": "Bologna car rentals — October",
    "prompt": "Car rental companies in Bologna with phone and website"
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Invalid format. Must be one of: markdown, html, text (or data_format markdown/screenshot, format raw)"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `404`:

```json
{
  "type": "error",
  "message": "Job not found"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Delete a dataset run's stored record

`DELETE /scraper/datasets/{jobId}/record`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/collectors-datasets.md)

Permanently removes the record and its stored rows. A run still in flight must be cancelled first (400). Irreversible. Free.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `jobId` | path | string | yes | Job id returned by the POST that started it. |

##### Examples

**curl**

```bash
curl -X DELETE 'https://api.quantumproxies.io/v1/scraper/datasets/job_8f2c1a/record' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.delete(
    'https://api.quantumproxies.io/v1/scraper/datasets/job_8f2c1a/record',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/datasets/job_8f2c1a/record", {
  method: "DELETE",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/datasets/job_8f2c1a/record');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'DELETE',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Deleted.
- `400` — The run is still running — cancel it first.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `404` — Unknown id — or an id that belongs to another account (ownership failures answer 404, never 403, so ids cannot be probed).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Dataset deleted",
  "payload": {
    "jobId": "ds_7a1c22"
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Cancel the run before deleting it."
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `404`:

```json
{
  "type": "error",
  "message": "Job not found"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

### Parser presets

Generate CSS selectors once with an LLM, replay them for free, let them self-heal.

#### Generate CSS selectors with an LLM, once

`POST /scraper/parser/generate`

**Price:** `parser_generate` $0.02 per call. parser_generate, only when coverage > 0. _(list of 2026-10-07)_  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/collectors-datasets.md)

Looks at a page once with a model and returns CSS selectors (`parser`) that extract the fields you asked for — ready to pass as `extract` on every later scrape of that layout, where no model is involved and the plain scrape price applies. Every selector is run against the page before it is returned: `report`, `missed` and `coverage` tell you which fields are reliable. Give `url` (fetched through the pool, `render: true` for SPA pages) or `html` you already have (max 3 MB, no proxy bandwidth). Charged once as a setup step, and only when the parser extracts something (`coverage` > 0).

##### Request body (required)

`Content-Type: application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `url` | string | no | Required unless `html` is given. |
| `html` | string | no |  |
| `fields` | object | no | field name → what it is. Required unless `prompt` is given. |
| `prompt` | string | no | Free-text alternative to `fields` (the model names them). |
| `render` | boolean | no |  Default `false`. |
| `country` | string | no | Country code or place name: letters, digits, spaces, dots, hyphens, underscores; max 56 characters. `all` means no targeting. |

Example:

```json
{
  "url": "https://example.com/products/42",
  "fields": {
    "title": "product name",
    "price": "current price with currency",
    "sku": "SKU code"
  }
}
```

##### Examples

**curl**

```bash
curl -X POST 'https://api.quantumproxies.io/v1/scraper/parser/generate' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://example.com/products/42","fields":{"title":"product name","price":"current price with currency","sku":"SKU code"}}'
```

**Python (requests)**

```python
import requests

r = requests.post(
    'https://api.quantumproxies.io/v1/scraper/parser/generate',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    json={
        "url": "https://example.com/products/42",
        "fields": {
            "title": "product name",
            "price": "current price with currency",
            "sku": "SKU code"
        }
    },
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/parser/generate", {
  method: "POST",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "url": "https://example.com/products/42",
    "fields": {
      "title": "product name",
      "price": "current price with currency",
      "sku": "SKU code"
    }
  })
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/parser/generate');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY', 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode([
    'url' => 'https://example.com/products/42',
    'fields' => [
      'title' => 'product name',
      'price' => 'current price with currency',
      'sku' => 'SKU code'
    ]
  ]),
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Parser generated (message says when no usable selector could be produced; then not charged).
- `400` — Malformed input. `message` names the parameter and the rule it broke.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `402` — The free tier plus wallet balance cannot cover the worst-case estimate of this call, or the account's monthly spend cap is reached. Nothing was run or charged.
- `403` — No proxy pool is available to serve the call (no house plan and no residential/datacenter/IPv6 plan on the account).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — Data API off for your key, or the generator's model is not configured.

Example `200` response:

```json
{
  "type": "response",
  "message": "Parser generated",
  "payload": {
    "parser": {
      "title": "h1.product-title",
      "price": {
        "selector": "span.price",
        "attr": null
      },
      "sku": "dd.sku"
    },
    "report": [
      {
        "field": "title",
        "selector": "h1.product-title",
        "sample": "Trail Runner 2",
        "missed": false
      },
      {
        "field": "price",
        "selector": "span.price",
        "sample": "€ 129,00",
        "missed": false
      },
      {
        "field": "sku",
        "selector": "dd.sku",
        "sample": "TR2-42",
        "missed": false
      }
    ],
    "missed": [],
    "coverage": 1,
    "repaired": false,
    "usage": {
      "cost_usd": 0.02,
      "free_usd": 0.02,
      "paid_usd": 0
    }
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Invalid format. Must be one of: markdown, html, text (or data_format markdown/screenshot, format raw)"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `402`:

```json
{
  "type": "error",
  "message": "Your free tier for this month is used up and your balance can't cover this call (estimated $0.002). Add pay-as-you-go credit or pick a plan — your API key stays the same: https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
  "payload": {
    "code": "INSUFFICIENT_FUNDS",
    "balance": 0,
    "free_remaining": 0,
    "estimated_cost": 0.002,
    "plans_url": "https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
    "billing_mode": "free_first"
  }
}
```

Example `403`:

```json
{
  "type": "error",
  "message": "Scraper API is not available: no proxy credentials found. Contact support or purchase a residential, datacenter or IPv6 plan."
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### List your presets

`GET /scraper/parser/presets`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/collectors-datasets.md)

Every stored parser of your account with its stats and version history. Free.

##### Examples

**curl**

```bash
curl -X GET 'https://api.quantumproxies.io/v1/scraper/parser/presets' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.get(
    'https://api.quantumproxies.io/v1/scraper/parser/presets',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/parser/presets", {
  method: "GET",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/parser/presets');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Presets.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Presets",
  "payload": {
    "presets": [
      {
        "id": "pst_7Qk3",
        "name": "example-product",
        "sourceUrl": "https://example.com/products/42",
        "fields": {
          "title": "product name",
          "price": "current price"
        },
        "render": false,
        "parser": {
          "title": "h1.product-title",
          "price": "span.price"
        },
        "version": 1,
        "autoHeal": true,
        "createdAt": 1759759331000,
        "updatedAt": 1759759331000,
        "lastHealAt": null,
        "stats": {
          "runs": 12,
          "lastRunAt": 1759830000000,
          "fields": {
            "title": {
              "hits": 12,
              "misses": 0
            },
            "price": {
              "hits": 11,
              "misses": 1
            }
          },
          "recent": [
            1,
            1,
            0.5,
            1
          ]
        },
        "history": [
          {
            "version": 1,
            "parser": {
              "title": "h1.product-title",
              "price": "span.price"
            },
            "at": 1759759331000,
            "reason": "created",
            "coverage": 1
          }
        ]
      }
    ]
  },
  "pagination": {}
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Save a parser as a preset

`POST /scraper/parser/presets`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/collectors-datasets.md)

Stores a parser (usually the one /scraper/parser/generate just returned) under a name. Scrape with `presetId` instead of an inline `extract` schema and every run is scored per field; when the recent success rate decays and `autoHeal` is on, the preset regenerates itself from `sourceUrl` and bumps its version. Ownership is server-side: a client-supplied tenant is ignored. Free to store.

##### Request body (required)

`Content-Type: application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | yes |  |
| `parser` | object (ExtractSchema) | yes | Structured-extraction schema: field name → CSS selector, or an object with `selector`, optional `attr` (attribute to read instead of text) and `all` (true = every match as an array). |
| `sourceUrl` | string | no | Page the parser was learned from — self-healing refetches it. |
| `fields` | object | no | field → description, so a heal can regenerate the same shape. |
| `render` | boolean | no |  Default `false`. |
| `autoHeal` | boolean | no |  |

Example:

```json
{
  "name": "example-product",
  "parser": {
    "title": "h1.product-title",
    "price": "span.price"
  },
  "sourceUrl": "https://example.com/products/42",
  "fields": {
    "title": "product name",
    "price": "current price"
  },
  "autoHeal": true
}
```

##### Examples

**curl**

```bash
curl -X POST 'https://api.quantumproxies.io/v1/scraper/parser/presets' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"name":"example-product","parser":{"title":"h1.product-title","price":"span.price"},"sourceUrl":"https://example.com/products/42","fields":{"title":"product name","price":"current price"},"autoHeal":true}'
```

**Python (requests)**

```python
import requests

r = requests.post(
    'https://api.quantumproxies.io/v1/scraper/parser/presets',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    json={
        "name": "example-product",
        "parser": {
            "title": "h1.product-title",
            "price": "span.price"
        },
        "sourceUrl": "https://example.com/products/42",
        "fields": {
            "title": "product name",
            "price": "current price"
        },
        "autoHeal": true
    },
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/parser/presets", {
  method: "POST",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "name": "example-product",
    "parser": {
      "title": "h1.product-title",
      "price": "span.price"
    },
    "sourceUrl": "https://example.com/products/42",
    "fields": {
      "title": "product name",
      "price": "current price"
    },
    "autoHeal": true
  })
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/parser/presets');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY', 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode([
    'name' => 'example-product',
    'parser' => [
      'title' => 'h1.product-title',
      'price' => 'span.price'
    ],
    'sourceUrl' => 'https://example.com/products/42',
    'fields' => [
      'title' => 'product name',
      'price' => 'current price'
    ],
    'autoHeal' => true
  ]),
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Created.
- `400` — Malformed input. `message` names the parameter and the rule it broke.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Preset created",
  "payload": {
    "id": "pst_7Qk3",
    "name": "example-product",
    "sourceUrl": "https://example.com/products/42",
    "fields": {
      "title": "product name",
      "price": "current price"
    },
    "render": false,
    "parser": {
      "title": "h1.product-title",
      "price": "span.price"
    },
    "version": 1,
    "autoHeal": true,
    "createdAt": 1759759331000,
    "updatedAt": 1759759331000,
    "lastHealAt": null,
    "stats": {
      "runs": 0,
      "lastRunAt": null,
      "fields": {},
      "recent": []
    },
    "history": [
      {
        "version": 1,
        "parser": {
          "title": "h1.product-title",
          "price": "span.price"
        },
        "at": 1759759331000,
        "reason": "created"
      }
    ]
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Invalid format. Must be one of: markdown, html, text (or data_format markdown/screenshot, format raw)"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Read a preset

`GET /scraper/parser/presets/{id}`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/collectors-datasets.md)

Parser, stats and version history. A preset belonging to someone else answers 404. Free.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Preset id returned when it was created. |

##### Examples

**curl**

```bash
curl -X GET 'https://api.quantumproxies.io/v1/scraper/parser/presets/pst_7Qk3' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.get(
    'https://api.quantumproxies.io/v1/scraper/parser/presets/pst_7Qk3',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/parser/presets/pst_7Qk3", {
  method: "GET",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/parser/presets/pst_7Qk3');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Preset.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `404` — Unknown id — or an id that belongs to another account (ownership failures answer 404, never 403, so ids cannot be probed).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Preset",
  "payload": {
    "id": "pst_7Qk3",
    "name": "example-product",
    "parser": {
      "title": "h1.product-title",
      "price": "span.price"
    },
    "version": 1,
    "autoHeal": true,
    "createdAt": 1759759331000,
    "updatedAt": 1759759331000,
    "lastHealAt": null,
    "stats": {
      "runs": 12,
      "lastRunAt": 1759830000000,
      "fields": {},
      "recent": []
    },
    "history": []
  },
  "pagination": {}
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `404`:

```json
{
  "type": "error",
  "message": "Job not found"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Update a preset

`PUT /scraper/parser/presets/{id}`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/collectors-datasets.md)

Rename, replace the parser (bumps the version), toggle `autoHeal`, update `fields`/`sourceUrl`. Only the keys you send change. Free.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Preset id returned when it was created. |

##### Request body (required)

`Content-Type: application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | no |  |
| `parser` | object (ExtractSchema) | no | Structured-extraction schema: field name → CSS selector, or an object with `selector`, optional `attr` (attribute to read instead of text) and `all` (true = every match as an array). |
| `autoHeal` | boolean | no |  |
| `fields` | object | no |  |
| `sourceUrl` | string | no |  |

Example:

```json
{
  "autoHeal": false
}
```

##### Examples

**curl**

```bash
curl -X PUT 'https://api.quantumproxies.io/v1/scraper/parser/presets/pst_7Qk3' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"autoHeal":false}'
```

**Python (requests)**

```python
import requests

r = requests.put(
    'https://api.quantumproxies.io/v1/scraper/parser/presets/pst_7Qk3',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    json={
        "autoHeal": false
    },
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/parser/presets/pst_7Qk3", {
  method: "PUT",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "autoHeal": false
  })
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/parser/presets/pst_7Qk3');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'PUT',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY', 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode([
    'autoHeal' => false
  ]),
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Updated.
- `400` — Malformed input. `message` names the parameter and the rule it broke.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `404` — Unknown id — or an id that belongs to another account (ownership failures answer 404, never 403, so ids cannot be probed).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Preset updated",
  "payload": {
    "id": "pst_7Qk3",
    "name": "example-product",
    "parser": {
      "title": "h1.product-title",
      "price": "span.price"
    },
    "version": 1,
    "autoHeal": false,
    "createdAt": 1759759331000,
    "updatedAt": 1759840000000,
    "lastHealAt": null,
    "stats": {
      "runs": 12,
      "lastRunAt": 1759830000000,
      "fields": {},
      "recent": []
    },
    "history": []
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Invalid format. Must be one of: markdown, html, text (or data_format markdown/screenshot, format raw)"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `404`:

```json
{
  "type": "error",
  "message": "Job not found"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Delete a preset

`DELETE /scraper/parser/presets/{id}`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/collectors-datasets.md)

Removes the preset and its version history. Scrapes that still send its `presetId` answer 404 afterwards. Free.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Preset id returned when it was created. |

##### Examples

**curl**

```bash
curl -X DELETE 'https://api.quantumproxies.io/v1/scraper/parser/presets/pst_7Qk3' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.delete(
    'https://api.quantumproxies.io/v1/scraper/parser/presets/pst_7Qk3',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/parser/presets/pst_7Qk3", {
  method: "DELETE",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/parser/presets/pst_7Qk3');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'DELETE',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Deleted.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `404` — Unknown id — or an id that belongs to another account (ownership failures answer 404, never 403, so ids cannot be probed).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Preset deleted",
  "payload": {
    "ok": true
  },
  "pagination": {}
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `404`:

```json
{
  "type": "error",
  "message": "Job not found"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Regenerate a preset's parser now

`POST /scraper/parser/presets/{id}/heal`

**Price:** `parser_generate` $0.02 per call. parser_generate only when healed is true. _(list of 2026-10-07)_  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/collectors-datasets.md)

The manual trigger of the repair that runs automatically when a preset decays: refetches `sourceUrl`, asks the model for fresh selectors and adopts them only if they extract more of the page than the current ones. `force: true` bypasses the cooldown between heals. Billed like a generation, and only when a new version was actually produced (`healed: true`).

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Preset id returned when it was created. |

##### Request body

`Content-Type: application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `force` | boolean | no |  Default `false`. |

Example:

```json
{
  "force": false
}
```

##### Examples

**curl**

```bash
curl -X POST 'https://api.quantumproxies.io/v1/scraper/parser/presets/pst_7Qk3/heal' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"force":false}'
```

**Python (requests)**

```python
import requests

r = requests.post(
    'https://api.quantumproxies.io/v1/scraper/parser/presets/pst_7Qk3/heal',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    json={
        "force": false
    },
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/parser/presets/pst_7Qk3/heal", {
  method: "POST",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "force": false
  })
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/parser/presets/pst_7Qk3/heal');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY', 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode([
    'force' => false
  ]),
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Heal attempted (message says whether a new version was adopted).
- `400` — Malformed input. `message` names the parameter and the rule it broke.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `402` — The free tier plus wallet balance cannot cover the worst-case estimate of this call, or the account's monthly spend cap is reached. Nothing was run or charged.
- `404` — Unknown id — or an id that belongs to another account (ownership failures answer 404, never 403, so ids cannot be probed).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — Data API off for your key, or the generator's model is not configured.

Example `200` response:

```json
{
  "type": "response",
  "message": "Preset healed",
  "payload": {
    "healed": true,
    "reason": "coverage improved",
    "version": 2,
    "coverageBefore": 0.5,
    "coverageAfter": 1,
    "parser": {
      "title": "h1[itemprop=name]",
      "price": "span.price-now"
    },
    "usage": {
      "cost_usd": 0.02,
      "free_usd": 0.02,
      "paid_usd": 0
    }
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Invalid format. Must be one of: markdown, html, text (or data_format markdown/screenshot, format raw)"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `402`:

```json
{
  "type": "error",
  "message": "Your free tier for this month is used up and your balance can't cover this call (estimated $0.002). Add pay-as-you-go credit or pick a plan — your API key stays the same: https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
  "payload": {
    "code": "INSUFFICIENT_FUNDS",
    "balance": 0,
    "free_remaining": 0,
    "estimated_cost": 0.002,
    "plans_url": "https://app.quantumproxies.io/plans?utm_source=api&utm_medium=402&utm_campaign=free-tier",
    "billing_mode": "free_first"
  }
}
```

Example `404`:

```json
{
  "type": "error",
  "message": "Job not found"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### How well a preset still works

`GET /scraper/parser/presets/{id}/stats`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/collectors-datasets.md)

Lifetime success rate per field, mean coverage over the recent window and whether the preset now counts as decayed (the signal that triggers self-healing). Free — it is the health check of a parser you already paid to build.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Preset id returned when it was created. |

##### Examples

**curl**

```bash
curl -X GET 'https://api.quantumproxies.io/v1/scraper/parser/presets/pst_7Qk3/stats' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.get(
    'https://api.quantumproxies.io/v1/scraper/parser/presets/pst_7Qk3/stats',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/parser/presets/pst_7Qk3/stats", {
  method: "GET",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/parser/presets/pst_7Qk3/stats');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Stats.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `404` — Unknown id — or an id that belongs to another account (ownership failures answer 404, never 403, so ids cannot be probed).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `503` — The Data API group is switched off for non-admin keys (early-access kill switch).

Example `200` response:

```json
{
  "type": "response",
  "message": "Preset stats",
  "payload": {
    "id": "pst_7Qk3",
    "name": "example-product",
    "version": 2,
    "runs": 12,
    "lastRunAt": 1759830000000,
    "successRateByField": {
      "title": 1,
      "price": 0.917
    },
    "recentCoverage": 0.96,
    "decayed": false,
    "autoHeal": true,
    "lastHealAt": 1759840000000
  },
  "pagination": {}
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `404`:

```json
{
  "type": "error",
  "message": "Job not found"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `503`:

```json
{
  "type": "error",
  "message": "This API is temporarily unavailable while we finalize pricing. Contact support for early access."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

## Proxies

Markdown for this group only: https://quantumproxies.io/docs/proxies.md

### Proxies

List your proxy plans, generate endpoint strings, manage IP whitelists.

#### List your proxy plans

`GET /public/proxies`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/proxies.md)

Every proxy service on the account (residential basic/premium/private, ISP, premium ISP, datacenter, datacenter traffic, IPv6, mobile, mobile v2) with credentials, bandwidth left, expiry and the `orderId` the generate and whitelist endpoints take. Free; no Data API billing.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `limit` | query | integer | no |  Default `50`. |
| `offset` | query | integer | no |  Default `0`. |
| `planType` | query | string: `residentialbasic`, `residentialpremium`, `resiprivate`, `isp`, `isppremium`, `datacenter`, `datacentertraffic`, `ipv6`, `mobile`, `mobile_v2` | no |  |
| `active` | query | string: `true`, `false` | no | Filter by expiry; omit for all. |

##### Examples

**curl**

```bash
curl -X GET 'https://api.quantumproxies.io/v1/public/proxies' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.get(
    'https://api.quantumproxies.io/v1/public/proxies',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/public/proxies", {
  method: "GET",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/public/proxies');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Plans.
- `400` — Malformed input. `message` names the parameter and the rule it broke.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.

Example `200` response:

```json
{
  "type": "response",
  "message": "Success",
  "payload": {
    "proxies": [
      {
        "id": "prx_1",
        "orderId": "ord_8c21",
        "planName": "Residential Premium 5 GB",
        "planType": "residentialpremium",
        "planTypeName": "Residential Premium",
        "username": "user_8c21",
        "password": "p4ssw0rd",
        "proxyId": "sub_19a",
        "bandwidth": 5,
        "bandwidthGB": 5,
        "bandwidthLeft": 3.2,
        "bandwidthLeftGB": 3.2,
        "bandwidthUsed": 1.8,
        "bandwidthUsedGB": 1.8,
        "bandwidthUsagePercent": 36,
        "isUnlimited": false,
        "expiry": "2026-11-06T00:00:00.000Z",
        "expiresAt": "2026-11-06T00:00:00.000Z",
        "isActive": true,
        "isExpired": false,
        "daysRemaining": 30,
        "hoursRemaining": 720,
        "ips": 0,
        "whitelistSlots": 0,
        "whitelistedIPs": [],
        "region": "Global",
        "speed": null,
        "highConcurrency": false,
        "highPriority": false,
        "createdAt": "2026-10-06T10:00:00.000Z"
      }
    ],
    "pagination": {
      "total": 1,
      "limit": 50,
      "offset": 0,
      "hasMore": false
    }
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Invalid format. Must be one of: markdown, html, text (or data_format markdown/screenshot, format raw)"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Generate proxy strings for a plan

`POST /public/proxies/generate`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/proxies.md)

Ready-to-use endpoint strings (credentials included) for one order, in HTTP or SOCKS5 and several formats, with the targeting the plan supports: country/state/city for residential and mobile, ISP code for Residential Premium and Mobile V2, ASN for Residential Basic and traffic-based Datacenter, static gateways for Datacenter. `rotation: sticky` keeps one IP for `sessionTime` minutes; `static` (IPv6 only) is a fixed session with no TTL. Mobile V2 can alternatively return the IP-auth proxy list for a whitelisted `ip`. Premium ISP orders still being provisioned answer 202 with `pending: true`. Which parameters apply depends on the plan type — unsupported ones are ignored, not rejected. Free; no Data API billing (the plan's own bandwidth is consumed when you use the proxies).

##### Request body (required)

`Content-Type: application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `orderId` | string | yes | From GET /public/proxies. |
| `protocol` | string: `http`, `socks5` | no |  Default `"http"`. |
| `format` | string: `user:pass@host:port`, `host:port:user:pass`, `http://user:pass@host:port`, `socks5://user:pass@host:port` | no | `ip:port:user:pass` and the `…@ip:port` spellings are accepted as aliases. Default `"user:pass@host:port"`. |
| `quantity` | integer | no |  Default `10`. |
| `country` | string | no | Lowercase country code (e.g. `us`); `all` = none. |
| `state` | string | no | Region slug (Residential Premium / Mobile V2: from the location tree). Alias `region`. |
| `city` | string | no | City slug. |
| `rotation` | string: `rotating`, `sticky`, `static` | no | `static` is IPv6 only. Default `"rotating"`. |
| `sessionTime` | integer | no | Sticky session minutes (Residential Basic / Datacenter traffic: min 3 enforced by the gateway). Default `10`. |
| `isp` | string | no | ISP code (Residential Premium / Mobile V2) or carrier ASN (Mobile). |
| `asn` | string | no | ASN, e.g. `AS12345` (Residential Basic / Datacenter traffic). |
| `strict` | boolean | no | Residential/Datacenter Basic: `true` allows location fallback. Default `false`. |
| `filter` | string: `speed`, `speed-quality`, `quality` | no | Residential Premium / Mobile V2 pool filter (default: max pool). |
| `isExtension` | boolean | no | Rewrite hostnames with a unique prefix so a browser extension cannot cache the proxy (needs the wildcard DNS of the gateway). Default `false`. |
| `ip` | string | no | Mobile V2: a whitelisted IP — returns the IP-auth proxy list instead of user:pass strings. |
| `gateway` | string: `ww`, `us`, `eu`, `as` | no | Mobile V2 region gateway. Default `"ww"`. |

Example:

```json
{
  "orderId": "ord_8c21",
  "protocol": "http",
  "format": "user:pass@host:port",
  "quantity": 5,
  "country": "us",
  "rotation": "sticky",
  "sessionTime": 10
}
```

##### Examples

**curl**

```bash
curl -X POST 'https://api.quantumproxies.io/v1/public/proxies/generate' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"orderId":"ord_8c21","protocol":"http","format":"user:pass@host:port","quantity":5,"country":"us","rotation":"sticky","sessionTime":10}'
```

**Python (requests)**

```python
import requests

r = requests.post(
    'https://api.quantumproxies.io/v1/public/proxies/generate',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    json={
        "orderId": "ord_8c21",
        "protocol": "http",
        "format": "user:pass@host:port",
        "quantity": 5,
        "country": "us",
        "rotation": "sticky",
        "sessionTime": 10
    },
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/public/proxies/generate", {
  method: "POST",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "orderId": "ord_8c21",
    "protocol": "http",
    "format": "user:pass@host:port",
    "quantity": 5,
    "country": "us",
    "rotation": "sticky",
    "sessionTime": 10
  })
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/public/proxies/generate');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY', 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode([
    'orderId' => 'ord_8c21',
    'protocol' => 'http',
    'format' => 'user:pass@host:port',
    'quantity' => 5,
    'country' => 'us',
    'rotation' => 'sticky',
    'sessionTime' => 10
  ]),
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Proxy strings.
- `202` — Premium ISP order still being provisioned.
- `400` — Missing orderId, expired plan, inactive sub-user, or a plan type that cannot generate.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `404` — Order (or its proxy) not found on this account.
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.
- `502` — The plan's network did not answer; retry.

Example `200` response:

```json
{
  "type": "response",
  "message": "Success",
  "payload": {
    "orderId": "ord_8c21",
    "quantity": 5,
    "protocol": "http",
    "format": "user:pass@host:port",
    "rotation": "sticky",
    "sessionTime": 10,
    "geoTargeting": {
      "country": "us",
      "state": null,
      "city": null
    },
    "proxies": [
      "user_8c21-country-us-session-a1b2c3-time-10:p4ssw0rd@residential-ww.quantumproxies.io:9999",
      "user_8c21-country-us-session-d4e5f6-time-10:p4ssw0rd@residential-ww.quantumproxies.io:9999"
    ],
    "bandwidth": 5,
    "bandwidthLeft": 3.2,
    "whitelist": []
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Proxy has expired"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `404`:

```json
{
  "type": "error",
  "message": "Order or proxy not found"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Example `502`:

```json
{
  "type": "error",
  "message": "Unable to generate proxies. Please try again."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Geolocate an IP

`GET /public/proxies/ip-info`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/proxies.md)

Country, region, city, coordinates, timezone, ISP and ASN of an IP address — useful to verify where a sticky session actually exits. Served by a third-party geolocation source with its own fair-use limit (about 45 lookups/minute shared). Free.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `ip` | query | string | yes |  |

##### Examples

**curl**

```bash
curl -X GET 'https://api.quantumproxies.io/v1/public/proxies/ip-info?ip=203.0.113.7' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.get(
    'https://api.quantumproxies.io/v1/public/proxies/ip-info?ip=203.0.113.7',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/public/proxies/ip-info?ip=203.0.113.7", {
  method: "GET",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/public/proxies/ip-info?ip=203.0.113.7');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — IP information.
- `400` — Missing `ip`, or the lookup source refused the address (private range, malformed).
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.

Example `200` response:

```json
{
  "type": "response",
  "message": "Success",
  "payload": {
    "ip": "203.0.113.7",
    "country": "United States",
    "countryCode": "US",
    "flag": "🇺🇸",
    "region": "California",
    "regionCode": "CA",
    "city": "Los Angeles",
    "zip": "90001",
    "lat": 34.05,
    "lon": -118.24,
    "timezone": "America/Los_Angeles",
    "isp": "Example Telecom",
    "org": "Example Telecom LLC",
    "as": "AS64496 Example Telecom"
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "IP address is required. Use ?ip=x.x.x.x"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### List whitelisted IPs of an order

`GET /public/proxies/whitelist-ip`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/proxies.md)

The IPs allowed to use an IP-auth plan without credentials. Mobile V2 plans also return the detailed upstream entries (ports, geo, sticky) under `entries`. Free.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orderId` | query | string | yes |  |

##### Examples

**curl**

```bash
curl -X GET 'https://api.quantumproxies.io/v1/public/proxies/whitelist-ip?orderId=ORDERID' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.get(
    'https://api.quantumproxies.io/v1/public/proxies/whitelist-ip?orderId=ORDERID',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/public/proxies/whitelist-ip?orderId=ORDERID", {
  method: "GET",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/public/proxies/whitelist-ip?orderId=ORDERID');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Whitelist.
- `400` — Malformed input. `message` names the parameter and the rule it broke.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `404` — Order not found on this account.
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.

Example `200` response:

```json
{
  "type": "response",
  "message": "Success",
  "payload": {
    "orderId": "ord_8c21",
    "whitelist_ip": [
      "203.0.113.7"
    ]
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Invalid format. Must be one of: markdown, html, text (or data_format markdown/screenshot, format raw)"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Whitelist an IP

`POST /public/proxies/whitelist-ip`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/proxies.md)

Allows an IPv4 address to use the plan by IP authentication. Supported on Residential Basic, Datacenter (both kinds), ISP, IPv6 and Mobile V2; Residential Premium/Private authenticate by user:pass and answer 200 with `whitelisted: false`. Mobile V2 accepts extra port/targeting options and `action: update` to edit an existing entry. Free.

##### Request body (required)

`Content-Type: application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `orderId` | string | yes |  |
| `ip` | string | yes | IPv4 address. |
| `action` | string: `add`, `update` | no | Mobile V2 only: `update` edits an existing entry's settings. Default `"add"`. |
| `ports_count` | integer | no | Mobile V2: ports to allocate. |
| `protocol` | string: `HTTP`, `SOCKS5` | no | Mobile V2. |
| `country` | string | no | Mobile V2 geo targeting for the allocated ports. |
| `region` | string | no | Mobile V2. |
| `city` | string | no | Mobile V2. |
| `isp` | string | no | Mobile V2. |
| `sticky` | boolean | no | Mobile V2: keep the same IP per port. |
| `ttl` | integer | no | Mobile V2: sticky session TTL in seconds. |

Example:

```json
{
  "orderId": "ord_8c21",
  "ip": "203.0.113.7"
}
```

##### Examples

**curl**

```bash
curl -X POST 'https://api.quantumproxies.io/v1/public/proxies/whitelist-ip' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"orderId":"ord_8c21","ip":"203.0.113.7"}'
```

**Python (requests)**

```python
import requests

r = requests.post(
    'https://api.quantumproxies.io/v1/public/proxies/whitelist-ip',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    json={
        "orderId": "ord_8c21",
        "ip": "203.0.113.7"
    },
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/public/proxies/whitelist-ip", {
  method: "POST",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "orderId": "ord_8c21",
    "ip": "203.0.113.7"
  })
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/public/proxies/whitelist-ip');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY', 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode([
    'orderId' => 'ord_8c21',
    'ip' => '203.0.113.7'
  ]),
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Whitelisted (or not applicable to this plan type).
- `400` — Missing fields, malformed IP, or the plan's network refused the entry.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `404` — Order not found on this account (or its residential sub-user is missing).
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.

Example `200` response:

```json
{
  "type": "response",
  "message": "IP whitelisted successfully",
  "payload": {
    "ip": "203.0.113.7",
    "whitelisted": true,
    "whitelist_ip": [
      "203.0.113.7"
    ]
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Invalid IP address format"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Remove a whitelisted IP

`DELETE /public/proxies/whitelist-ip`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/proxies.md)

Removes the IP from the plan's IP-auth list (Mobile V2 also accepts the entry `id`). Plans that authenticate by user:pass answer 200 with `removed: false`. Free.

##### Request body (required)

`Content-Type: application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `orderId` | string | yes |  |
| `ip` | string | yes |  |
| `id` | string | no | Mobile V2 entry id (alternative handle). |

Example:

```json
{
  "orderId": "ord_8c21",
  "ip": "203.0.113.7"
}
```

##### Examples

**curl**

```bash
curl -X DELETE 'https://api.quantumproxies.io/v1/public/proxies/whitelist-ip' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"orderId":"ord_8c21","ip":"203.0.113.7"}'
```

**Python (requests)**

```python
import requests

r = requests.delete(
    'https://api.quantumproxies.io/v1/public/proxies/whitelist-ip',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    json={
        "orderId": "ord_8c21",
        "ip": "203.0.113.7"
    },
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/public/proxies/whitelist-ip", {
  method: "DELETE",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "orderId": "ord_8c21",
    "ip": "203.0.113.7"
  })
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/public/proxies/whitelist-ip');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'DELETE',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY', 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode([
    'orderId' => 'ord_8c21',
    'ip' => '203.0.113.7'
  ]),
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Removed (or not applicable).
- `400` — Malformed input. `message` names the parameter and the rule it broke.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `404` — Order not found on this account.
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.
- `500` — Our side: the scraper service is unavailable or the call timed out. Never billed.

Example `200` response:

```json
{
  "type": "response",
  "message": "IP removed from whitelist",
  "payload": {
    "ip": "203.0.113.7",
    "removed": true,
    "whitelist_ip": []
  },
  "pagination": {}
}
```

Example `400`:

```json
{
  "type": "error",
  "message": "Invalid format. Must be one of: markdown, html, text (or data_format markdown/screenshot, format raw)"
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Example `500`:

```json
{
  "type": "error",
  "message": "Scraper service unavailable. Please try again later."
}
```

Try it: https://app.quantumproxies.io/data-api/playground

## Account

Markdown for this group only: https://quantumproxies.io/docs/account.md

### Account

Usage, billing status and platform settings.

#### Per-day usage and cost

`GET /scraper/usage`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/account.md)

Request counts (total and per query type), proxy bandwidth, AI token cost and billed vs free-tier spend per UTC day for the calling key's account, read from the daily aggregation. Free; not subject to the Data API kill switch.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `days` | query | integer | no |  Default `30`. |

##### Examples

**curl**

```bash
curl -X GET 'https://api.quantumproxies.io/v1/scraper/usage' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.get(
    'https://api.quantumproxies.io/v1/scraper/usage',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/usage", {
  method: "GET",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/usage');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Usage report.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.

Example `200` response:

```json
{
  "type": "response",
  "message": "Scraper usage",
  "payload": {
    "days": [
      {
        "date": "2026-10-06",
        "requests": 143,
        "by_type": {
          "extract": 120,
          "google": 20,
          "map": 3
        },
        "proxy_kb": 61230,
        "ai_cost_usd": 0,
        "billed_usd": 0,
        "free_usd": 0.0375
      }
    ],
    "totals": {
      "requests": 143,
      "by_type": {
        "extract": 120,
        "google": 20,
        "map": 3
      },
      "proxy_kb": 61230,
      "billed_usd": 0,
      "free_usd": 0.0375
    },
    "period_days": 30
  },
  "pagination": {}
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### Billing status and your price list

`GET /scraper/billing`

**Price:** No charge.  
**Rate limit:** Per API key, by tier (see Rate limits and tiers).  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/account.md)

Current price list with your tier's discount applied (keyed like pricing.json), monthly free allowance and what is left of it, wallet balance, active tier, billing mode and the per-minute limits each mode carries. Lets a client or an agent budget its calls without a dashboard round-trip. Free; not subject to the Data API kill switch.

##### Examples

**curl**

```bash
curl -X GET 'https://api.quantumproxies.io/v1/scraper/billing' \
  -H 'Authorization: Bearer qp_live_YOUR_API_KEY'
```

**Python (requests)**

```python
import requests

r = requests.get(
    'https://api.quantumproxies.io/v1/scraper/billing',
    headers={"Authorization": "Bearer qp_live_YOUR_API_KEY"},
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/scraper/billing", {
  method: "GET",
  headers: {
    Authorization: "Bearer qp_live_YOUR_API_KEY"
  }
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/scraper/billing');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer qp_live_YOUR_API_KEY'],
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Billing status.
- `401` — Missing, malformed, unknown, disabled or expired API key; or the account is not active.
- `429` — Key rate limit (hourly window), plan per-minute budget, browser-render concurrency, or pool capacity. Back off on Retry-After.

Example `200` response:

```json
{
  "type": "response",
  "message": "Billing status",
  "payload": {
    "billing_enabled": true,
    "billed": true,
    "balance": 12.5,
    "tier": {
      "key": "payg",
      "name": "Pay as you go",
      "discount_pct": 0,
      "rate_limit_per_min": 20
    },
    "billing_mode": {
      "preference": "free_first",
      "effective": "free_first",
      "available": true,
      "free_first_rate_limit_per_min": 20,
      "balance_rate_limit_per_min": 300,
      "effective_rate_limit_per_min": 20
    },
    "free_monthly_usd": 2,
    "free_remaining_usd": 1.62,
    "ai_token_markup": 2,
    "prices_usd": {
      "extract": 0.0002,
      "extract_render": 0.001,
      "serp": 0.0005,
      "serp_render": 0.002,
      "map": 0.0005,
      "seo_audit": 0.0012,
      "crawl_page": 0.0003,
      "batch_url": 0.0002,
      "collector_result": 0.002
    }
  },
  "pagination": {}
}
```

Example `401`:

```json
{
  "type": "error",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qp_live_YOUR_API_KEY"
}
```

Example `429`:

```json
{
  "type": "error",
  "message": "Rate limit exceeded. Resets in 23 minutes.",
  "payload": {
    "rateLimit": 1200,
    "requestCount": 1200,
    "resetAt": "2026-10-07T15:00:00.000Z"
  }
}
```

Try it: https://app.quantumproxies.io/data-api/playground

#### No-key MCP trial settings

`GET /mcp/trial-config`

**Price:** No charge.  
**Rate limit:** No rate limit.  
[Try it in the Playground](https://app.quantumproxies.io/data-api/playground) · [Markdown for this group](https://quantumproxies.io/docs/account.md)

What the keyless trial of the hosted MCP endpoint allows right now for a brand: whether it is on, calls per client address per UTC day, calls across all addresses per day, and which tools it may run. Read by the hosted MCP servers once a minute; nothing secret in it, so it needs no API key and is cacheable for 60 s. The brand comes from `?brand=` or, failing that, from the request host.

##### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `brand` | query | string: `QP`, `QD` | no | QP = QuantumProxies, QD = QuanticData. Defaults to the brand of the host you call. |

##### Examples

**curl**

```bash
curl -X GET 'https://api.quantumproxies.io/v1/mcp/trial-config'
```

**Python (requests)**

```python
import requests

r = requests.get(
    'https://api.quantumproxies.io/v1/mcp/trial-config',
    timeout=120,
)
data = r.json()
if data["type"] != "response":
    raise SystemExit(data["message"])
print(data["payload"])
```

**Node (fetch)**

```javascript
const res = await fetch("https://api.quantumproxies.io/v1/mcp/trial-config", {
  method: "GET"
});
const data = await res.json();
if (data.type !== "response") throw new Error(data.message);
console.log(data.payload);
```

**PHP (curl)**

```php
<?php
$ch = curl_init('https://api.quantumproxies.io/v1/mcp/trial-config');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_RETURNTRANSFER => true,
]);
$raw = curl_exec($ch);
curl_close($ch);
$data = json_decode($raw, true);
if ($data['type'] !== 'response') { throw new RuntimeException($data['message']); }
print_r($data['payload']);
```

##### Responses

- `200` — Trial settings.

Example `200` response:

```json
{
  "type": "response",
  "message": "ok",
  "payload": {
    "brand": "QD",
    "enabled": true,
    "perIpPerDay": 5,
    "perDay": 300,
    "tools": [
      "scrape",
      "search",
      "search_and_read",
      "map",
      "seo_audit",
      "list_collectors",
      "collector_run_status",
      "proxy_locations"
    ]
  }
}
```

Try it: https://app.quantumproxies.io/data-api/playground
