Give this endpoint 1 to 3 sample URLs that share the same page layout, plus a schema or a plain-English prompt describing what you want. The model writes a reusable extract_rules object once, validates it across your sample pages (per-page validation report plus a matched_fields verdict), and returns it ready to paste into Scrape
. From then on, /scrape runs those rules on every future page with that layout for fetch credits only: no more model calls, no extraction surcharge. This is the cheap path at volume.
HTTP Request
1
2
| POST /ai-generate-extraction-rules
GET /ai-generate-extraction-rules
|
Both verbs run the same handler and bill the same. On GET, pass urls comma-separated (up to 3, URL-encode each) and schema as a JSON-object string. Use POST for long URL lists or big schemas.
Parameters
| Parameter | Type | Required | Default | Description |
|---|
urls | array of strings | Yes | — | 1 to 3 sample URLs sharing the same page layout. Rules are written from the first reachable (non-blocked) URL; the rest only validate the rules. Put your best sample first. |
schema | object | No* | null | The fields you want, as a full JSON Schema or compact shorthand, e.g. {"products": [{"title": "string", "price": "number", "url": "url"}]}. Leaf types: string, number, integer, boolean, url. Max 50 leaf fields, nesting depth 3. Required unless prompt is given. |
prompt | string | No* | null | Plain-English description of what to extract (max 2000 chars). Required unless schema is given. Prefer schema when you can: it makes the response validatable per field. |
render_js | string | No | auto | auto, true, or false. auto tries plain HTTP first and escalates to a headless browser only when blocked. Only the winning attempt is billed. |
proxy_type | string | No | datacenter | datacenter or isp. ISP exits are static-residential addresses with higher trust on strict targets, at a higher fetch price. |
session_id | string | No | generated | Sticky-session handle (1-64 chars, A-Za-z0-9_-). The same value always exits from the same IP. |
cookies | object, string, or array | No | null | Cookies to send: {"a": "1"}, "a=1; b=2", or the cookie list returned by /unlock. HTTP path only. |
user_agent | string | No | null | Custom User-Agent; also selects the matching TLS/header fingerprint. HTTP path only. |
headers | object | No | {} | Extra request headers. Reserved names (Host, Cookie, User-Agent, Authorization, …) are rejected; use cookies / user_agent instead. |
method | string | No | GET | GET or POST for fetching the sample pages. |
body | string | No | null | Request body for the fetch. Only with method=POST and a Content-Type in headers. |
wait_for | string | No | null | CSS selector to wait for before capture. Browser path only. |
timeout | integer | No | 20 | Total request budget in seconds (max 27). Recommended: 25. At the default, the fetch and first model call usually eat the whole budget, so the repair pass (the normal recovery when the first pass misses fields) rarely gets to run. |
* At least one of schema or prompt is required.
There is no url, fields, or source parameter on this endpoint. It always reads a cleaned-up copy of the page markup, because selectors need the underlying HTML, not just visible text.
The Reuse Workflow
POST /ai-generate-extraction-rules with 1 to 3 sample URLs of the same layout and a schema.- Take
data.extract_rules from the response. POST /scrape on any page with that layout, passing it as extract_rules. Fetch credits only, no further AI cost, no further model calls.
Credits
Sum of fetch credits for every non-blocked sample URL, plus 15 generation credits when extract_rules was produced and matched at least one field on at least one page (matched_fields is all or some). Rules that matched nothing anywhere (matched_fields: "none") are still returned so you can inspect what was tried, but the 15 generation credits are not billed. If every URL is blocked: 0 credits and ai_error: "blocked".
Fetch credits per URL follow the /scrape table: datacenter 1 (HTTP) / 5 (browser), isp 5 (HTTP) / 20 (browser). At most two model calls happen per request regardless of URL count, so 3 sample URLs do not cost 3x the generation price.
Response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| {
"status": true,
"request_id": "a35488c9-6d45-3b35-e7a4-f9e75acd",
"data": {
"extract_rules": {
"title": "h1.product-title",
"price": {"selector": ".price", "output": "text"}
},
"sample": {"title": "14-inch Ultrabook", "price": "899.00"},
"validation": [
{"url": "https://shop.example/p/14-ultrabook", "http_status": 200, "render_used": "http", "blocked": false, "fetch_error": null, "empty_fields": [], "list_counts": {}},
{"url": "https://shop.example/p/16-workstation", "http_status": 200, "render_used": "http", "blocked": false, "fetch_error": null, "empty_fields": [], "list_counts": {}}
],
"matched_fields": "all",
"warnings": [],
"ai_error": null,
"credits": 17,
"elapsed_ms": 3820,
"session_id": "5e4cdb8ab2a90424",
"proxy_type": "datacenter"
}
}
|
Response Fields
| Field | Type | Description |
|---|
status | boolean | Whether the request was successful. false when the AI step failed (error mirrors data.ai_error). |
request_id | string | Unique identifier for the request. |
data.extract_rules | object | null | Paste-ready for /scrape’s extract_rules parameter. null when generation failed (see ai_error). |
data.sample | object | null | Result of running the rules on the first (primary) page: a preview of what /scrape will return. |
data.validation | array | One entry per URL you sent, produced by the same deterministic engine /scrape uses. |
data.validation[].url | string | The sample URL this entry reports on. |
data.validation[].http_status | integer | null | HTTP status the page returned. null when the fetch itself failed. |
data.validation[].render_used | string | null | http or browser: which path fetched this page. null when the fetch failed. |
data.validation[].blocked | boolean | Whether the target served an anti-bot challenge instead of content. |
data.validation[].fetch_error | string | null | Set when this URL’s fetch failed: one of the fetch-failure slugs (dns_error, connection_refused, timeout, …), plus unsupported_content or target_not_allowed. That page took no part in rule validation. |
data.validation[].empty_fields | array | Dotted paths (e.g. products[].price) that matched nothing on this page. For list fields, a nested field surfaces here only when it is empty on every item of the list. |
data.validation[].list_counts | object | Item counts per list field on this page. |
data.matched_fields | string | null | Verdict over the rules’ data-carrying fields: all (every field matched on at least one page), some (at least one field never matched, see warnings), or none (no field matched anywhere, generation not billed). null when no rules were produced. |
data.warnings | array | Human-readable notes, e.g. "price matched on 2/3 pages" or "input truncated to token budget". |
data.ai_error | string | null | Set when the AI step failed. Same values as /ai-extract. |
data.credits | integer | Credits billed for this call (fetch sum + 15 generation when applicable). 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 (yours, or a generated one). Reuse it to keep the same IP. |
data.proxy_type | string | Proxy pool this request exited from. |
Error Responses
| Status | Description |
|---|
400 | Invalid request: bad URL, more than 3 urls, both schema and prompt missing, schema breaking a cap, prompt over 2000 chars, out-of-range timeout, or a URL pointing at a non-public address. Not billed. |
415 | The URL returned a non-textual document, e.g. image/png |
502 | A target could not be fetched: DNS failure, connection refused, TLS error, timeout. Body: could not fetch target: <reason>. Not billed. |
503 | Our infrastructure is unavailable: proxy pool failure, browser pool exhausted, or a requested proxy_type pool not configured. Not billed. Target-side failures are always 502, never 503. |
Generation failures (the pages fetched fine but the model produced no usable rules) are returned as HTTP 200 with status: false and data.ai_error set, never as an HTTP error status.
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
| import requests
url = "https://ai-web-scraper-api1.p.rapidapi.com/ai-generate-extraction-rules"
payload = {
"urls": [
"https://shop.example/p/14-ultrabook",
"https://shop.example/p/16-workstation"
],
"schema": {"title": "string", "price": "number"},
"timeout": 25
}
headers = {
"Content-Type": "application/json",
"X-RapidAPI-Key": "YOUR_API_KEY",
"X-RapidAPI-Host": "ai-web-scraper-api1.p.rapidapi.com"
}
response = requests.post(url, json=payload, headers=headers)
data = response.json()["data"]
print("Verdict:", data["matched_fields"])
print("Rules:", data["extract_rules"])
print("Sample:", data["sample"])
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| const url = "https://ai-web-scraper-api1.p.rapidapi.com/ai-generate-extraction-rules";
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-RapidAPI-Key": "YOUR_API_KEY",
"X-RapidAPI-Host": "ai-web-scraper-api1.p.rapidapi.com",
},
body: JSON.stringify({
urls: [
"https://shop.example/p/14-ultrabook",
"https://shop.example/p/16-workstation",
],
schema: { title: "string", price: "number" },
timeout: 25,
}),
});
const { data } = await response.json();
console.log("Verdict:", data.matched_fields);
console.log("Rules:", data.extract_rules);
console.log("Sample:", data.sample);
|
1
2
3
4
5
6
7
8
9
10
11
12
| curl -X POST "https://ai-web-scraper-api1.p.rapidapi.com/ai-generate-extraction-rules" \
-H "Content-Type: application/json" \
-H "X-RapidAPI-Key: YOUR_API_KEY" \
-H "X-RapidAPI-Host: ai-web-scraper-api1.p.rapidapi.com" \
-d '{
"urls": [
"https://shop.example/p/14-ultrabook",
"https://shop.example/p/16-workstation"
],
"schema": {"title": "string", "price": "number"},
"timeout": 25
}'
|
Reusing the Rules on /scrape
Take data.extract_rules from the response above and pass it to /scrape on any page with the same layout. Fetch credits only, no model call:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| import requests
rules = data["extract_rules"] # from the generation response above
response = requests.post(
"https://ai-web-scraper-api1.p.rapidapi.com/scrape",
json={"url": "https://shop.example/p/17-tablet", "extract_rules": rules},
headers={
"Content-Type": "application/json",
"X-RapidAPI-Key": "YOUR_API_KEY",
"X-RapidAPI-Host": "ai-web-scraper-api1.p.rapidapi.com",
},
)
print(response.json()["data"]["extract"])
# {"title": "17-inch Tablet", "price": "499.00"}
|
- Scrape
— Run the generated
extract_rules on any page with the same layout, for fetch credits only - AI Extract
— One-off AI extraction from a single page, no rules needed