curl --request POST \
--url 'https://api.zenrows.com/v1/extract/prepared-domains?apikey=' \
--header 'Content-Type: application/json' \
--data '
{
"domain": "scrapingcourse.com"
}
'import requests
url = "https://api.zenrows.com/v1/extract/prepared-domains?apikey="
payload = { "domain": "scrapingcourse.com" }
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({domain: 'scrapingcourse.com'})
};
fetch('https://api.zenrows.com/v1/extract/prepared-domains?apikey=', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));const url = 'https://api.zenrows.com/v1/extract/prepared-domains?apikey=';
const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({domain: 'scrapingcourse.com'})
};
fetch(url, options)
.then(res => res.json())
.then(json => console.log(json))
.catch(err => console.error(err));package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.zenrows.com/v1/extract/prepared-domains?apikey="
payload := strings.NewReader("{\n \"domain\": \"scrapingcourse.com\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.zenrows.com/v1/extract/prepared-domains?apikey=",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'domain' => 'scrapingcourse.com'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}require 'uri'
require 'net/http'
url = URI("https://api.zenrows.com/v1/extract/prepared-domains?apikey=")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"domain\": \"scrapingcourse.com\"\n}"
response = http.request(request)
puts response.read_bodyHttpResponse<String> response = Unirest.post("https://api.zenrows.com/v1/extract/prepared-domains?apikey=")
.header("Content-Type", "application/json")
.body("{\n \"domain\": \"scrapingcourse.com\"\n}")
.asString();{
"domain": "scrapingcourse.com",
"status": "preparing",
"state": "received",
"submitted_at": "2026-10-15T10:00:00Z",
"updated_at": "2026-10-15T10:00:00Z"
}{
"code": "invalid_argument",
"detail": "<string>",
"reason": "<string>",
"status": 123,
"title": "<string>",
"type": "<string>"
}{
"code": "AUTH002",
"detail": "Invalid API key.",
"instance": "/v1",
"status": 401,
"title": "Invalid apikey provided (AUTH002)",
"type": "https://docs.zenrows.com/api-error-codes#AUTH002"
}{
"code": "invalid_argument",
"detail": "<string>",
"reason": "<string>",
"status": 123,
"title": "<string>",
"type": "<string>"
}{
"code": "invalid_argument",
"detail": "<string>",
"reason": "<string>",
"status": 123,
"title": "<string>",
"type": "<string>"
}Ask for a domain to be prepared for Extract
Submit a domain for preparation, within a monthly allowance. The submission is accepted immediately and works through discovery, preparation and verification, typically in minutes: poll getExtractDomainStatus instead of blocking. Send 1 to 5 example_urls when you can; they replace discovery. One works; we recommend 3 or more of the same page type (e.g. 3 product pages), because Extract compares pages of one type to check the fields it found and fill in fields one page is missing. Several page types can share the 5 (e.g. 3 product pages + 2 search-result pages). Submitting a domain that is already prepared returns its current record rather than starting again, so it does not add new page types. Send an Idempotency-Key header to make a retry safe. Costs no credits. Documented at https://docs.zenrows.com/extract/endpoints.
curl --request POST \
--url 'https://api.zenrows.com/v1/extract/prepared-domains?apikey=' \
--header 'Content-Type: application/json' \
--data '
{
"domain": "scrapingcourse.com"
}
'import requests
url = "https://api.zenrows.com/v1/extract/prepared-domains?apikey="
payload = { "domain": "scrapingcourse.com" }
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({domain: 'scrapingcourse.com'})
};
fetch('https://api.zenrows.com/v1/extract/prepared-domains?apikey=', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));const url = 'https://api.zenrows.com/v1/extract/prepared-domains?apikey=';
const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({domain: 'scrapingcourse.com'})
};
fetch(url, options)
.then(res => res.json())
.then(json => console.log(json))
.catch(err => console.error(err));package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.zenrows.com/v1/extract/prepared-domains?apikey="
payload := strings.NewReader("{\n \"domain\": \"scrapingcourse.com\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.zenrows.com/v1/extract/prepared-domains?apikey=",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'domain' => 'scrapingcourse.com'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}require 'uri'
require 'net/http'
url = URI("https://api.zenrows.com/v1/extract/prepared-domains?apikey=")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"domain\": \"scrapingcourse.com\"\n}"
response = http.request(request)
puts response.read_bodyHttpResponse<String> response = Unirest.post("https://api.zenrows.com/v1/extract/prepared-domains?apikey=")
.header("Content-Type", "application/json")
.body("{\n \"domain\": \"scrapingcourse.com\"\n}")
.asString();{
"domain": "scrapingcourse.com",
"status": "preparing",
"state": "received",
"submitted_at": "2026-10-15T10:00:00Z",
"updated_at": "2026-10-15T10:00:00Z"
}{
"code": "invalid_argument",
"detail": "<string>",
"reason": "<string>",
"status": 123,
"title": "<string>",
"type": "<string>"
}{
"code": "AUTH002",
"detail": "Invalid API key.",
"instance": "/v1",
"status": 401,
"title": "Invalid apikey provided (AUTH002)",
"type": "https://docs.zenrows.com/api-error-codes#AUTH002"
}{
"code": "invalid_argument",
"detail": "<string>",
"reason": "<string>",
"status": 123,
"title": "<string>",
"type": "<string>"
}{
"code": "invalid_argument",
"detail": "<string>",
"reason": "<string>",
"status": 123,
"title": "<string>",
"type": "<string>"
}Authorizations
Fetch, Extract, Crawl and the account endpoint accept the key as the apikey query parameter. Keys are per account and self-serve: create one in the Zenrows dashboard (app.zenrows.com), or, for an agent with no human present, POST https://app.zenrows.com/api/agent/signup.
Headers
The same key returns the original submission instead of spending a second allowance slot.
Body
The domain to prepare, without scheme.
"scrapingcourse.com"
1 to 5 URLs on that domain. One works; we recommend 3 or more of the same page type (e.g. 3 product pages), because Extract compares pages of one type to check the fields it found and fill in fields one page is missing. Several page types can share the 5 (e.g. 3 product pages + 2 search-result pages). When present, they replace discovery.
[ "https://www.scrapingcourse.com/ecommerce/product/abominable-hoodie/", "https://www.scrapingcourse.com/ecommerce/product/adrienne-trek-jacket/", "https://www.scrapingcourse.com/ecommerce/product/aeon-capri/" ]
Response
Accepted. The body is the submission in the same shape getExtractDomainStatus returns.
A domain's preparation for Extract.
Whether the domain can be used yet. Branch on this.
preparing, ready, failed The current step. Gains steps over time.
"received"
"discovering"
"preparing"
"verifying"
"ready"
"failed"
Progress per page, with its page type in role. Only present for the account that submitted the domain.
Show child attributes
Show child attributes
Present when status is failed.
Show child attributes
Show child attributes
Was this page helpful?