Fetch a page and pull the fields you describe out of it. No selectors: an extraction model reads the page and returns JSON in data.json. Describe what you want with a schema (the exact shape you need), a prompt (what to look for, in words), or both; at least one is required. Every field is nullable: when the page does not contain a value, the model returns null instead of guessing. It is a good fit for one-off pages or layouts you have not mapped yet.
HTTP Request
Both POST (JSON body) and GET (query-string parameters) work, run the same handler, and bill the same. POST is recommended: schema payloads are JSON objects and URLs should stay short. On GET, send schema as a URL-encoded JSON object string.
Parameters
| Parameter | Type | Required | Default | Description |
|---|
url | string | Yes | — | Absolute http(s) URL of the page to fetch. Must point at a public address. |
schema | object | No* | null | What to extract: a JSON Schema (object at the top level) or the compact shorthand described below. Required unless prompt is given. |
prompt | string | No* | null | Free-text instructions or hints, e.g. only in-stock items, prices in EUR. Max 2000 characters. Required unless schema is given. With prompt alone the model chooses the JSON shape. |
source | string | No | "auto" | What the extraction step reads: markdown (visible text, cheaper, fine for text-only fields), html (a cleaned-up copy of the page that keeps links, images, and attributes), or auto, which picks markdown unless the schema or prompt asks for a URL, link, or image field. |
render_js | string | No | "auto" | false: plain HTTP fetch. true: headless-browser rendering. auto: HTTP first, escalating to the browser when the response looks blocked; only the winning attempt is billed. |
proxy_type | string | No | "datacenter" | Proxy pool: datacenter or isp (static-residential addresses, higher trust with strict targets, higher fetch cost). |
session_id | string | No | generated | Sticky-session handle (1-64 chars, A-Za-z0-9_-). The same value always maps to the same outbound IP. Whatever was used is echoed in data.session_id. |
cookies | object, string, or list | No | null | Cookies to send: {"a": "1"}, "a=1; b=2", or the cookie list returned by /unlock. Applied on the HTTP path only. |
user_agent | string | No | null | User-Agent to send; also selects the matching TLS/header fingerprint. HTTP path only. |
headers | object | No | {} | Extra request headers. Reserved names such as Host, Cookie, User-Agent, and Authorization are rejected; use cookies and user_agent instead. |
method | string | No | "GET" | HTTP method for the fetch: GET or POST. |
body | string | No | null | Request body. Only allowed with method: "POST" and a Content-Type in headers. |
wait_for | string | No | null | CSS selector to wait for before capturing the page. Browser path only; at most 15 seconds and never past timeout. |
timeout | integer | No | 25 | Total budget in seconds (1-27). The fetch gets timeout - 8s; the remainder goes to the extraction step. |
fields | string or list | No | null | Allow-list of data keys to return, e.g. json,credits. Unknown names are rejected before any fetch. |
* At least one of schema or prompt is required.
Schema shorthand
schema accepts a full JSON Schema, or a compact shorthand:
1
| {"products": [{"title": "string", "price": "number", "url": "url"}]}
|
- Leaf types:
string, number, integer, boolean, url. - Lists:
[{...}] with exactly one element describing the item shape. - Every field is nullable: the model returns
null when the page does not have it, never an invented value. - Caps: 50 leaf fields total, nesting depth 3.
Use schema when you can: it makes the response validatable. prompt alone works too, but the model picks the JSON shape and there is no per-field validation.
Credits
Fetch credits (the same proxy_type by render_used table as /scrape) plus 5 extraction credits when json is produced:
| Outcome | proxy_type=datacenter | proxy_type=isp |
|---|
Served over HTTP, json produced | 1 + 5 = 6 | 5 + 5 = 10 |
Served by the headless browser, json produced | 5 + 5 = 10 | 20 + 5 = 25 |
| Target blocked the request | 0 | 0 |
Extraction failed (ai_error set) | fetch credits only | fetch credits only |
The extraction surcharge is billed only when output was actually produced. A blocked target costs 0 credits and no extraction is attempted.
Response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| {
"status": true,
"request_id": "a35488c9-6d45-3b35-e7a4-f9e75acd",
"data": {
"url": "https://shop.example/category/laptops",
"http_status": 200,
"json": {
"products": [
{"title": "14-inch Ultrabook", "price": 899.0, "url": "https://shop.example/p/14-ultrabook"},
{"title": "16-inch Workstation", "price": 1899.0, "url": "https://shop.example/p/16-workstation"}
]
},
"ai_error": null,
"blocked": false,
"block_reason": null,
"detected_protection": null,
"render_used": "http",
"credits": 6,
"elapsed_ms": 2140,
"session_id": "5e4cdb8ab2a90424",
"proxy_type": "datacenter"
}
}
|
Response Fields
| Field | Type | Description |
|---|
status | boolean | true when extraction succeeded; false when the AI step failed (a top-level error then carries the same slug as data.ai_error) |
request_id | string | Unique identifier for the request |
data.url | string | Final URL after redirects |
data.http_status | integer | HTTP status the target returned, never masked |
data.json | object or null | The extracted object. null when extraction did not run or failed; see data.ai_error |
data.ai_error | string or null | blocked, llm_timeout, llm_error, or invalid_output; null when json was produced |
data.blocked | boolean | The target served an anti-bot challenge or rejection instead of content |
data.block_reason | string or null | Stable slug for the block, e.g. forbidden, captcha, cloudflare_challenge |
data.detected_protection | string or null | Detected anti-bot vendor (cloudflare, akamai, datadome, perimeterx, incapsula); advisory, filled even when not blocked |
data.render_used | string | Which path produced the answer: http or browser |
data.credits | integer | Credits billed for this call; same value as the X-RapidAPI-Billing header |
data.elapsed_ms | integer | Total server-side time for this call |
data.session_id | string | Sticky-session handle used; reuse it to keep the same IP |
data.proxy_type | string | Proxy pool this request went out of |
Error Responses
Extraction failures never turn into an HTTP error. You always get a 200 with the fetch metadata, status: false, a top-level error, and data.ai_error set; the fetch is billed, the extraction is not:
1
2
3
4
5
6
7
8
9
10
11
12
13
| {
"status": false,
"request_id": "a35488c9-6d45-3b35-e7a4-f9e75acd",
"error": "invalid_output",
"data": {
"url": "https://shop.example/category/laptops",
"http_status": 200,
"json": null,
"ai_error": "invalid_output",
"blocked": false,
"credits": 1
}
}
|
ai_error | Meaning |
|---|
blocked | The target blocked the request; no extraction was attempted |
llm_timeout | Not enough of the time budget was left to finish extraction; the fetch is billed, the extraction is not |
llm_error | The extraction step could not run |
invalid_output | Extraction ran but its output did not match the requested schema |
HTTP error statuses (not billed):
| Code | When |
|---|
400 | Invalid request: bad URL, both schema and prompt missing, schema breaking a cap, prompt over 2000 characters, out-of-range timeout, or a URL pointing at a non-public address |
415 | The URL returned a non-textual document, e.g. image/png |
502 | The target could not be fetched: DNS failure, connection refused, TLS error, timeout |
503 | Our infrastructure is temporarily unavailable (proxy or browser pool); the target is fine, retry later |
Code Examples
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| import requests
url = "https://ai-web-scraper-api1.p.rapidapi.com/ai-extract"
payload = {
"url": "https://shop.example/category/laptops",
"schema": {
"products": [
{"title": "string", "price": "number", "url": "url"}
]
}
}
headers = {
"X-RapidAPI-Key": "YOUR_API_KEY",
"X-RapidAPI-Host": "ai-web-scraper-api1.p.rapidapi.com",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
data = response.json()["data"]
if data["json"]:
for product in data["json"]["products"]:
print(f"{product['title']}: {product['price']}")
print(f" {product['url']}\n")
else:
print(f"Extraction failed: {data['ai_error']}")
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| const url = "https://ai-web-scraper-api1.p.rapidapi.com/ai-extract";
const response = await fetch(url, {
method: "POST",
headers: {
"X-RapidAPI-Key": "YOUR_API_KEY",
"X-RapidAPI-Host": "ai-web-scraper-api1.p.rapidapi.com",
"Content-Type": "application/json",
},
body: JSON.stringify({
url: "https://shop.example/category/laptops",
schema: {
products: [{ title: "string", price: "number", url: "url" }],
},
}),
});
const { data } = await response.json();
if (data.json) {
data.json.products.forEach((product) => {
console.log(`${product.title}: ${product.price}`);
console.log(` ${product.url}\n`);
});
} else {
console.log(`Extraction failed: ${data.ai_error}`);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
| curl -X POST "https://ai-web-scraper-api1.p.rapidapi.com/ai-extract" \
-H "X-RapidAPI-Key: YOUR_API_KEY" \
-H "X-RapidAPI-Host: ai-web-scraper-api1.p.rapidapi.com" \
-H "Content-Type: application/json" \
-d '{
"url": "https://shop.example/category/laptops",
"schema": {
"products": [
{"title": "string", "price": "number", "url": "url"}
]
}
}'
|
- Generate Extraction Rules
— Same input, but writes a reusable
extract_rules object from sample pages; the cheap path at volume (fetch credits only on every reuse) - Scrape
— Fetch a URL and get its content plus optional CSS/XPath extraction
- AI Summarize
— Fetch a URL and get a text summary instead of structured JSON