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
BASE_URL="https://scraper.2captcha.com"
API_KEY="<YOUR_API_KEY>"
Send the request:
bash
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
{
"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
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
statusin the JSON matches the expected target page status; bodyis not empty and contains the expected HTML;x-debug.response_idis populated;x-debug.status_codematches the outer HTTP status;x-debug.pricecontains the actual task cost.
If you only need the HTML without JSON:
bash
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
https://scraper.2captcha.com
bash
BASE_URL="https://scraper.2captcha.com"
3. General Request Rules
- Use
Content-Type: application/jsonfor POST requests. - Method parameters are passed flat in the JSON body, without a nested
paramsobject. task_typecan 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
emailandpasswordfields are used only for authorization and are not stored with the task. - The actual cost depends on the method, plan, and environment. Check the
pricefield in thex-debugheader.
Correct:
json
{
"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
{
"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
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
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
{
"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
408 Request Timeout
With format: "json":
json
{
"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
422 Unprocessable Entity
Example:
json
{
"error": "max_restarts_exceeded"
}
6. Asynchronous Execution
The asynchronous scenario consists of two steps:
- create a task via
POST /tasks/request; - get the result via
GET /tasks/result/:response_id.
6.1. Creating a Task: POST /tasks/request
Example:
bash
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
201 Created
json
{
"response_id": "0193f2a4-1b2c-7d3e-8f4a-5b6c7d8e9f0a"
}
With format: "raw", the body contains only the ID:
text
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
{
"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
{
"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
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
pending
With 404:
text
Not found
With 410:
text
Result expired
With 422:
text
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
{
"waitFor": "{\"text\":\"Loaded\"}"
}
not as a nested object:
json
{
"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
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
{
"status": 200,
"headers": {
"content-type": "text/html; charset=utf-8"
},
"body": "<!DOCTYPE html>..."
}
7.4. Get HTML Directly
bash
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
<!DOCTYPE html>
<html>
7.5. Get Markdown
JSON:
json
{
"task_type": "scrape",
"url": "https://example.com",
"data_format": "markdown",
"format": "json"
}
Direct:
json
{
"task_type": "scrape",
"url": "https://example.com",
"data_format": "markdown",
"format": "raw"
}
Markdown example:
md
# Example Domain
[More information](https://www.iana.org/domains/example)
7.6. Take a Viewport Screenshot
Base64 in JSON:
json
{
"task_type": "scrape",
"url": "https://example.com",
"data_format": "screenshot",
"format": "json",
"fullPage": false
}
Response:
json
{
"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
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
89 50 4E 47 0D 0A 1A 0A
7.8. Take a Full-Page Screenshot
json
{
"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
{
"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
{
"task_type": "scrape",
"url": "https://example.com",
"waitFor": "{\"element\":\"#content\",\"checkVisible\":false}"
}
Wait for a Visible Element
json
{
"task_type": "scrape",
"url": "https://example.com",
"waitFor": "{\"element\":\"#content\",\"checkVisible\":true}"
}
Wait for Full Load
json
{
"task_type": "scrape",
"url": "https://example.com",
"waitFor": "{\"state\":\"load\"}"
}
Wait for DOM Construction
json
{
"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
{
"task_type": "scrape",
"url": "https://example.com/account",
"cdpurl": "wss://browser.example.com/devtools/browser/9b2c1f0a-..."
}
Requirements:
ws://orwss://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 |
8.3. Regular Web Search
bash
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
{
"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.
8.4. Image Search
json
{
"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
https://www.google.com/search?q=modern+furniture&hl=en&gl=us&udm=2
8.5. News
json
{
"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
{
"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
{
"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
{
"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
{
"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
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
[
{
"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
{
"error": "Insufficient balance"
}
With format: "raw", the API may return plain text only:
text
Insufficient balance
Always check:
- the API's HTTP status;
- the
Content-Type; x-debug.status_code;x-debug.response_id;x-debug.price;- for Scrape JSON — the separate
statusof the target page.
11. Utility Endpoints
No authorization required.
GET /health
bash
curl "$BASE_URL/health"
json
{
"status": "ok",
"ts": 1747983214000
}
GET /health/ready
bash
curl "$BASE_URL/health/ready"
Successful response:
json
{
"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 theresponse_idand request the result later. - Do not treat an outer HTTP
200as confirmation that the target site also responded with200. - 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-levelbody. - 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.