Scraper API — User Guide

This guide describes how to work with the Scraper API directly over HTTP: creating tasks, synchronous execution, retrieving results, viewing history, fetching web pages, and Google search.


Quick Start — get the HTML of a page

The simplest scenario is to synchronously fetch a page and get its HTML as JSON.

You will need:

  • the API address;
  • an API key;
  • the page URL.

Set the API address and key:

bash Copy
BASE_URL="https://scraper.2captcha.com"
API_KEY="<YOUR_API_KEY>"

Send the request:

bash Copy
curl -i -X POST "$BASE_URL/tasks/sync" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "task_type": "scrape",
    "url": "https://example.com",
    "data_format": "raw",
    "format": "json"
  }'

Task parameters:

Field Value Purpose
task_type scrape fetch a web page
url https://example.com target page address
data_format raw return the raw HTML
format json put the result in JSON

A successful response has HTTP status 200 and a body roughly like this:

json Copy
{
  "status": 200,
  "headers": {
    "content-type": "text/html; charset=utf-8"
  },
  "body": "<!doctype html><html>...</html>"
}

Here:

  • status — the HTTP status of the target page;
  • headers — the target page's response headers;
  • body — the resulting HTML.

Task metadata is not in the body but in the x-debug response header:

http Copy
x-debug: {"response_id":"...","add_datetime":1747983214000,"finish_datetime":1747983220000,"price":0.0005,"status_code":200,"status_message":"OK"}

Check that:

  • the Scraper API's outer HTTP status is 200;
  • the status in the JSON matches the expected target page status;
  • body is not empty and contains the expected HTML;
  • x-debug.response_id is populated;
  • x-debug.status_code matches the outer HTTP status;
  • x-debug.price contains the actual task cost.

If you only need the HTML without JSON:

bash Copy
curl -X POST "$BASE_URL/tasks/sync" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "task_type": "scrape",
    "url": "https://example.com",
    "data_format": "raw",
    "format": "raw"
  }' \
  --output page.html

The result will be saved to page.html.


1. API Address

Production:

text Copy
https://scraper.2captcha.com
bash Copy
BASE_URL="https://scraper.2captcha.com"

2. Authorization

The API supports two authorization methods. One method is sufficient per request.

2.1. API Key

The recommended method:

http Copy
Authorization: Bearer <API_KEY>

Example:

bash Copy
curl -X POST "$BASE_URL/tasks/sync" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "task_type": "scrape",
    "url": "https://example.com"
  }'

Do not pass the API key in the URL, do not store it in a repository, and do not attach it to logs or bug reports.

2.2. Email and Password

For POST requests, email and password are passed in the JSON body:

json Copy
{
  "email": "user@example.com",
  "password": "secret"
}

For GET requests, they are passed in the query string:

text Copy
GET /task_history?email=user@example.com&password=secret

This method is less secure for GET requests: the URL can end up in the client's history, proxy logs, and server logs. Use an API key whenever possible.

2.3. Per-Endpoint Requirements

Endpoint Authorization
POST /tasks/request required
POST /tasks/sync required
GET /task_history required
GET /tasks/result/:response_id not required

With invalid or missing credentials, the API returns:

http Copy
401 Unauthorized

3. General Request Rules

  • Use Content-Type: application/json for POST requests.
  • Method parameters are passed flat in the JSON body, without a nested params object.
  • task_type can be passed in the body or in the query string. If specified in both places, the body takes priority.
  • The JSON body size must not exceed 10,000 bytes.
  • The email and password fields are used only for authorization and are not stored with the task.
  • The actual cost depends on the method, plan, and environment. Check the price field in the x-debug header.

Correct:

json Copy
{
  "task_type": "scrape",
  "url": "https://example.com",
  "data_format": "raw"
}

Do not use a nested params object unless a separate API version explicitly requires it:

json Copy
{
  "task_type": "scrape",
  "params": {
    "url": "https://example.com"
  }
}

4. The x-debug Header

All API responses contain the x-debug header. It holds metadata for the task and the API response itself:

http Copy
x-debug: {"response_id":"0193f2a4-1b2c-7d3e-8f4a-5b6c7d8e9f0a","add_datetime":1747983214000,"finish_datetime":1747983220000,"price":0.0005,"status_code":200,"status_message":"OK"}
Field Type Description
response_id string or null task identifier; null if the task was not created
add_datetime number or null task creation time, Unix milliseconds
finish_datetime number or null completion time; null while the task is running
price number actual execution cost
status_code number Scraper API HTTP status
status_message string HTTP status text

The API's HTTP status and the target page's status are different values:

text Copy
HTTP 200 from Scraper API
└── status: 404 inside the result — the target site responded with 404

5. Synchronous Execution: POST /tasks/sync

The endpoint creates a task and waits for it to complete within a single HTTP request.

5.1. General Parameters

Field Type Required Default Description
task_type string yes method: scrape or google_search
format json or raw no json format of the method result
timeout number no 60 wait time in seconds, allowed range 1–120
email string when authorizing by password user email
password string when authorizing by password user password
coordinates object no { "lat": number, "lon": number, "radius"?: number }
uule string no Canonical Name or lat,lon[,radius]
cdpurl string no WebSocket URL of your own Chrome/CDP instance
other fields depends on the method depends on the method scrape or google_search parameters

5.2. Successful Response

On successful completion, the API returns:

  • HTTP 200;
  • the task result in the body;
  • task metadata in x-debug.

For scrape with format: "json", the actual structure used is:

json Copy
{
  "status": 200,
  "headers": {
    "content-type": "text/html; charset=utf-8"
  },
  "body": "<!DOCTYPE html>..."
}

For format: "raw", the contents of body are returned directly: HTML, Markdown, or a binary PNG.

5.3. Timeout

If the task does not complete within the specified time:

http Copy
408 Request Timeout

With format: "json":

json Copy
{
  "error": "timeout",
  "response_id": "0193f2a4-1b2c-7d3e-8f4a-5b6c7d8e9f0a"
}

The task continues running. Use the received response_id for a subsequent /tasks/result/:response_id request.

5.4. Execution Error

If the task finished with an error:

http Copy
422 Unprocessable Entity

Example:

json Copy
{
  "error": "max_restarts_exceeded"
}

6. Asynchronous Execution

The asynchronous scenario consists of two steps:

  1. create a task via POST /tasks/request;
  2. get the result via GET /tasks/result/:response_id.

6.1. Creating a Task: POST /tasks/request

Example:

bash Copy
curl -X POST "$BASE_URL/tasks/request" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "task_type": "scrape",
    "url": "https://example.com",
    "data_format": "raw",
    "format": "json"
  }'

Successful response:

http Copy
201 Created
json Copy
{
  "response_id": "0193f2a4-1b2c-7d3e-8f4a-5b6c7d8e9f0a"
}

With format: "raw", the body contains only the ID:

text Copy
0193f2a4-1b2c-7d3e-8f4a-5b6c7d8e9f0a

6.2. Webhook

For an asynchronous task, you can specify:

Field Type Description
webhook_url string notification URL
webhook_method GET or POST defaults to GET
webhook_data string or object custom webhook data

Example:

json Copy
{
  "task_type": "scrape",
  "url": "https://example.com",
  "webhook_url": "https://client.example.com/scrape-callback",
  "webhook_method": "POST",
  "webhook_data": {
    "order_id": "A-10042"
  }
}

For POST, the API sends:

json Copy
{
  "status": 200,
  "response_id": "0193f2a4-1b2c-7d3e-8f4a-5b6c7d8e9f0a",
  "request_url": "https://client.example.com/scrape-callback",
  "status_message": "OK",
  "webhook_data": {
    "order_id": "A-10042"
  }
}

Webhook delivery errors are logged but do not change the task result.

6.3. Retrieving the Result

bash Copy
curl "$BASE_URL/tasks/result/$RESPONSE_ID"

The endpoint does not require authorization and returns the raw result contents.

Status Meaning
200 OK task completed successfully
202 Accepted task is still running
404 Not Found ID not found
410 Gone result deleted after retention period expired
422 Unprocessable Entity task finished with an error

With 202:

text Copy
pending

With 404:

text Copy
Not found

With 410:

text Copy
Result expired

With 422:

text Copy
max_restarts_exceeded

Besides x-debug, this endpoint also returns x-debug_request with the parameters of the found task.


7. The scrape Method

The method fetches a single web page and returns HTML, Markdown, or a screenshot.

7.1. Parameters

Field Type Required Default Description
task_type string yes always scrape
url string yes full URL with http:// or https://
data_format string no raw raw, markdown, or screenshot
format string no json json or raw
waitFor string containing JSON no wait condition
fullPage boolean no false full-page screenshot; applies to screenshot
cdpurl string no your own Chrome via CDP

waitFor must be passed as a string:

json Copy
{
  "waitFor": "{\"text\":\"Loaded\"}"
}

not as a nested object:

json Copy
{
  "waitFor": {
    "text": "Loaded"
  }
}

7.2. data_format

Value format: "json" format: "raw"
raw JSON with status, headers, HTML in body HTML directly
markdown JSON with Markdown in body Markdown directly
screenshot JSON with Base64 PNG in body binary PNG

7.3. Get HTML as JSON

bash Copy
curl -X POST "$BASE_URL/tasks/sync" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "task_type": "scrape",
    "url": "https://example.com",
    "data_format": "raw",
    "format": "json"
  }'

Response:

json Copy
{
  "status": 200,
  "headers": {
    "content-type": "text/html; charset=utf-8"
  },
  "body": "<!DOCTYPE html>..."
}

7.4. Get HTML Directly

bash Copy
curl -X POST "$BASE_URL/tasks/sync" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "task_type": "scrape",
    "url": "https://example.com",
    "data_format": "raw",
    "format": "raw"
  }'

The response begins with:

html Copy
<!DOCTYPE html>
<html>

7.5. Get Markdown

JSON:

json Copy
{
  "task_type": "scrape",
  "url": "https://example.com",
  "data_format": "markdown",
  "format": "json"
}

Direct:

json Copy
{
  "task_type": "scrape",
  "url": "https://example.com",
  "data_format": "markdown",
  "format": "raw"
}

Markdown example:

md Copy
# Example Domain

[More information](https://www.iana.org/domains/example)

7.6. Take a Viewport Screenshot

Base64 in JSON:

json Copy
{
  "task_type": "scrape",
  "url": "https://example.com",
  "data_format": "screenshot",
  "format": "json",
  "fullPage": false
}

Response:

json Copy
{
  "status": 200,
  "headers": {
    "content-type": "text/html; charset=utf-8"
  },
  "body": "iVBORw0KGgoAAAANSUhEUg..."
}

The body field contains Base64 without the data:image/png;base64, prefix.

7.7. Get a Binary PNG

bash Copy
curl -X POST "$BASE_URL/tasks/sync" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "task_type": "scrape",
    "url": "https://example.com",
    "data_format": "screenshot",
    "format": "raw"
  }' \
  --output screenshot.png

The first eight bytes of the file:

text Copy
89 50 4E 47 0D 0A 1A 0A

7.8. Take a Full-Page Screenshot

json Copy
{
  "task_type": "scrape",
  "url": "https://example.com/long-page",
  "data_format": "screenshot",
  "format": "json",
  "fullPage": true
}

Use a genuinely long page. To verify the result, decode the PNG and make sure both the top and bottom of the page are present in the image.

For data_format: "raw", the fullPage parameter is effectively ignored and does not change the HTML result.

7.9. waitFor

Wait for Text

json Copy
{
  "task_type": "scrape",
  "url": "https://example.com/dynamic",
  "waitFor": "{\"text\":\"Content loaded\"}"
}

The page is suitable for this test only if the string is absent from the initial HTML and is added later.

Wait for an Element

json Copy
{
  "task_type": "scrape",
  "url": "https://example.com",
  "waitFor": "{\"element\":\"#content\",\"checkVisible\":false}"
}

Wait for a Visible Element

json Copy
{
  "task_type": "scrape",
  "url": "https://example.com",
  "waitFor": "{\"element\":\"#content\",\"checkVisible\":true}"
}

Wait for Full Load

json Copy
{
  "task_type": "scrape",
  "url": "https://example.com",
  "waitFor": "{\"state\":\"load\"}"
}

Wait for DOM Construction

json Copy
{
  "task_type": "scrape",
  "url": "https://example.com",
  "waitFor": "{\"state\":\"domcontentloaded\"}"
}

Supported state values:

Value Description
load the page and its dependent resources have loaded
domcontentloaded the DOM is built, resources may still be loading

7.10. Your Own Chrome via cdpurl

json Copy
{
  "task_type": "scrape",
  "url": "https://example.com/account",
  "cdpurl": "wss://browser.example.com/devtools/browser/9b2c1f0a-..."
}

Requirements:

  • ws:// or wss:// is supported;
  • the browser must be reachable by the worker for the entire execution;
  • the task uses that browser's cookies, sessions, profile, fingerprint, and proxy;
  • do not publish the CDP URL: access to it effectively grants access to the browser session.

8. The google_search Method

The method performs a Google query and returns structured results or Markdown.

8.1. Parameters

Field Type Required Default Description
task_type string yes always google_search
url string yes full Google Search URL with parameters
format json or raw no json JSON with extra information, or the result directly
data_format json or markdown no json format of the result field
page_num integer no 1 number of result pages
with_html boolean no false add page HTML; only for format: "json"
coordinates object no { "lat": number, "lon": number, "radius"?: number }
uule string no Canonical Name or lat,lon[,radius]

8.2. Parameters Inside url

Parameter Example Description
q q=fastify+nodejs search query
hl hl=en language of the results page, two-letter code
gl gl=us search country, two-letter ISO code
tbm tbm=isch search type
udm udm=14 alternative search type
uule uule=Paris... geolocation in the URL

tbm values:

Value Type
isch images
vid video
nws news
shop shopping
bks books
lcl local results/maps

udm values:

Value Type
2 images
7 video
8 jobs
12 news
14 classic web without AI answers
18 forums
28 shopping
36 books
39 short-form video
50 AI Mode
56 clean web
bash Copy
curl -X POST "$BASE_URL/tasks/sync" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "task_type": "google_search",
    "url": "https://www.google.com/search?q=fastify+nodejs&hl=en&gl=us",
    "page_num": 1,
    "format": "json",
    "data_format": "json",
    "with_html": false
  }'

Documented result example:

json Copy
{
  "url": "https://www.google.com/search?q=fastify+nodejs&hl=en&gl=us",
  "html": [],
  "data": {
    "total": 100000,
    "organic": [
      {
        "title": "Fastify",
        "url": "https://fastify.dev/",
        "description": "Fast and low overhead web framework for Node.js"
      }
    ]
  }
}

If with_html: true, the html field contains the HTML of the fetched pages.

json Copy
{
  "task_type": "google_search",
  "url": "https://www.google.com/search?q=modern+furniture&hl=en&gl=us&tbm=isch",
  "format": "json",
  "data_format": "json"
}

Alternatively:

text Copy
https://www.google.com/search?q=modern+furniture&hl=en&gl=us&udm=2

8.5. News

json Copy
{
  "task_type": "google_search",
  "url": "https://www.google.com/search?q=technology&hl=en&gl=us&tbm=nws",
  "format": "json",
  "data_format": "json"
}

8.6. Result in Markdown

json Copy
{
  "task_type": "google_search",
  "url": "https://www.google.com/search?q=fastify+nodejs&hl=en&gl=us",
  "format": "json",
  "data_format": "markdown"
}

8.7. Geo-Targeting

Via Canonical Name:

json Copy
{
  "task_type": "google_search",
  "url": "https://www.google.com/search?q=pizza&hl=en&gl=us",
  "uule": "New York,New York,United States"
}

Via coordinates:

json Copy
{
  "task_type": "google_search",
  "url": "https://www.google.com/search?q=pizza&hl=en&gl=us",
  "coordinates": {
    "lat": 40.7128,
    "lon": -74.006,
    "radius": 10
  }
}

Or:

json Copy
{
  "task_type": "google_search",
  "url": "https://www.google.com/search?q=pizza&hl=en&gl=us",
  "uule": "40.7128,-74.0060,10"
}

If the radius is not specified, uule with coordinates defaults to a 200 km radius.


9. Task History

bash Copy
curl "$BASE_URL/task_history?task_type=scrape&from=2026-01-01&to=2026-12-31&limit=50&offset=0" \
  -H "Authorization: Bearer $API_KEY"
Parameter Type Default Description
task_type string filter by method
from DateTime lower bound of add_datetime
to DateTime upper bound of add_datetime
limit integer 100 number of records
offset integer 0 offset

Response:

json Copy
[
  {
    "response_id": "0193f2a4-1b2c-7d3e-8f4a-5b6c7d8e9f0a",
    "add_datetime": 1747983214000,
    "finish_datetime": 1747983220000,
    "task_type": "scrape",
    "params": {
      "task_type": "scrape",
      "url": "https://example.com",
      "format": "json"
    },
    "price": 0.0005,
    "mime_type": "application/json",
    "error": ""
  }
]

10. Common Errors

HTTP Status Cause
400 Bad Request invalid parameters, unknown or disabled task_type, body larger than 10,000 bytes
401 Unauthorized authorization missing or invalid
402 Payment Required insufficient balance
408 Request Timeout synchronous wait ended; the task may still be running
410 Gone result deleted after retention period
422 Unprocessable Entity task finished with an error
503 Service Unavailable infrastructure not ready; related to the readiness check

Example error in JSON:

json Copy
{
  "error": "Insufficient balance"
}

With format: "raw", the API may return plain text only:

text Copy
Insufficient balance

Always check:

  1. the API's HTTP status;
  2. the Content-Type;
  3. x-debug.status_code;
  4. x-debug.response_id;
  5. x-debug.price;
  6. for Scrape JSON — the separate status of the target page.

11. Utility Endpoints

No authorization required.

GET /health

bash Copy
curl "$BASE_URL/health"
json Copy
{
  "status": "ok",
  "ts": 1747983214000
}

GET /health/ready

bash Copy
curl "$BASE_URL/health/ready"

Successful response:

json Copy
{
  "status": "ready",
  "deps": {
    "redis_streams": true,
    "redis_cache": true,
    "clickhouse": true,
    "s3": true
  }
}

If a dependency is unavailable, the endpoint returns 503 and status: "degraded".


12. Integration Recommendations

  • Use the synchronous endpoint for short tasks and the asynchronous one for long or bulk tasks.
  • On 408, do not immediately create a duplicate task: save the response_id and request the result later.
  • Do not treat an outer HTTP 200 as confirmation that the target site also responded with 200.
  • Do not rely on a fixed price hardcoded in your code; read x-debug.price.
  • For PNGs with format: "raw", save the response as a binary file.
  • For PNGs with format: "json", decode the Base64 from the top-level body.
  • For waitFor.text, use a string that is absent from the original HTML and appears without user action.
  • Do not use external demo sites as permanent fixtures: their content and availability may change.
  • Protect API keys, passwords, webhook URLs, and CDP URLs.