Crawling API
Получайте любой URL через резидентную прокси-сеть Crawlbase с опциональным JavaScript-рендерингом, решением бот-челленджей и гео-маршрутизацией. Универсальный endpoint, на котором построено всё остальное.
Как это работает
Каждый запрос Crawling API принимает целевой URL и возвращает страницу, которую этот целевой сайт отдал бы реальному браузеру в нужной географии, с нужным профилем устройства, после того как любые анти-бот челленджи будут разрешены. На каждом вызове последовательно происходят три вещи:
- Маршрутизация. Запрос отправляется через резидентный или дата-центровый exit-узел — автоматически по умолчанию или в конкретной стране, если вы передадите
country=. Доступны sticky-сессии, чтобы последовательность вызовов использовала один и тот же IP. - Рендеринг. Если вы аутентифицируетесь через JavaScript token, URL загружается в реальном headless-браузере. Контролы page-wait, scroll, click и AJAX-idle позволяют дождаться фактического контента, а не начальной HTML-оболочки.
- Обход анти-бот защиты. Cloudflare, PerimeterX, DataDome, hCaptcha и другие распространённые челленджи решаются на стороне сервера. Вы получаете HTML после прохождения челленджа, а не страницу с челленджем.
Один и тот же endpoint покрывает все три случая. Передавайте только нужные параметры — нет отдельного «JS-rendering API» или «anti-bot API». Если вы не передаёте параметры, доступные только JS-токену, запрос идёт по дешёвому, быстрому пути; как только передаёте — запрос переключается на путь рендеринга. Цена за успешный ответ одинакова в обоих случаях.
Токены
Аутентификация использует один из двух типов токенов — оба находятся в одной учётной записи и оба аутентифицируют один и тот же endpoint:
- Normal Token (TCP) — для статических HTML- или JSON-ответов, когда браузер не нужен. Быстрее, дешевле, используется для большинства простых целей скрейпинга.
- JavaScript Token — для SPA, приложений на React/Vue/Angular, лениво подгружаемых лент и любых целей, скрывающих контент за клиентским рендерингом. Обязателен для использования
page_wait,ajax_wait,scrollиcss_click_selector.
Если запрос с Normal-токеном возвращает пустое тело или 525 (челлендж не удалось решить), стандартное решение — повторить запрос на JavaScript-токене. Большинству современных целей нужен браузер, даже когда их начальный HTML выглядит полным. См. Authentication для полного процесса управления токенами.
Параллельность и тарификация
Каждый запрос, возвращающий pc_status: 200, учитывается в вашей месячной квоте. Неуспешные запросы (таймауты, блокировки, 5xx от целевого сайта) бесплатны — повторы против нестабильного источника не преподнесут сюрприз в счёте. Лимиты параллельности масштабируются с вашим тарифом; ответ включает заголовок remaining, который можно использовать для проактивного снижения нагрузки до достижения лимита. Долгие краулы (тяжёлый JS-рендеринг, большой page_wait) должны использовать async-режим ниже, чтобы освободить слот параллельности в момент постановки запроса в очередь.
Клиентские таймауты. Среднее время ответа — 4–10 секунд на запрос, но запросы из «хвоста» латентности (тяжёлые SPA, scroll_interval=60, медленные источники) могут занять больше. Установите клиентский таймаут как минимум на 90 секунд, чтобы легитимные медленные ответы не отваливались по таймауту до их прихода.
Другие рекомендации на стороне клиента. Отправляйте Accept-Encoding: gzip в каждом запросе — полезные нагрузки нетривиальны (полные HTML-страницы или markdown), и gzip обычно сжимает их до трети размера на проводе. Если вы используете Scrapy, отключите DNS-кеш, чтобы хост API оставался резолвимым на протяжении долгих краулов.
Endpoint
# All requests are GET. The url parameter must be fully URL-encoded.
# Body is returned as the target page's content (HTML, JSON, image, etc).
# Metadata is returned as response headers (pc_status, original_status, url, rid).Быстрый старт
curl 'https://api.crawlbase.com/?token=YOUR_TOKEN&url=https%3A%2F%2Fgithub.com%2Fanthropic'from crawlbase import CrawlingAPI
api = CrawlingAPI({'token': 'YOUR_TOKEN'})
res = api.get('https://github.com/anthropic')
print(res['body'])const { CrawlingAPI } = require('crawlbase');
const api = new CrawlingAPI({ token: 'YOUR_TOKEN' });
const res = await api.get('https://github.com/anthropic');
console.log(res.body);require 'crawlbase'
api = Crawlbase::API.new(token: 'YOUR_TOKEN')
res = api.get('https://github.com/anthropic')
puts res.body<?php
use Crawlbase\CrawlingAPI;
$api = new CrawlingAPI(['token' => 'YOUR_TOKEN']);
$res = $api->get('https://github.com/anthropic');
echo $res->body;package main
import (
"fmt"
"github.com/crawlbase/crawlbase-go"
)
func main() {
api, _ := crawlbase.NewCrawlingAPI("YOUR_TOKEN")
res, _ := api.Get("https://github.com/anthropic", nil)
fmt.Println(res.Body)
}Запрос
Каждый запрос Crawling API — это один HTTP-вызов к endpoint. Большинство запросов — GET: передайте параметры строки запроса ниже, чтобы управлять рендерингом, гео, форматом вывода и async-поведением. Используйте POST, когда нужно отправить форму или JSON-тело, и PUT — для загрузки сырых данных.
Параметры запроса
Все параметры передаются как значения строки запроса. Обязательны только token и url.
Обязательные
http:// или https://).Маршрутизация и гео
Выберите, откуда исходит запрос и какое устройство видит целевой сайт. Маршрутизация важна для интернет-магазинов, поисковой выдачи и любых сайтов, локализующих контент по IP — немецкий каталог Amazon недоступен с американского exit-узла даже при правильном URL, а Google SERP локализуется одновременно по географии и URL-параметрам hl/gl в сочетании с IP. Задайте country явно — и автоматически появятся правильная валюта, язык и ассортимент.
US, GB, DE, JP, …) для маршрутизации краула через exit-узлы этой страны. По умолчанию — автоматический выбор гео..onion-сайты. Не включайте для целей в clearnet — Tor-выходы медленнее и шумнее, чем резидентный пул.Crawlbase может переопределить параметр country, чтобы автоматически выбрать прокси на основе URL — это даёт лучший показатель успешности на большинстве сайтов. Свяжитесь с поддержкой, если вам нужно отключить автоматический выбор прокси.
Указание страны может уменьшить число успешных запросов, поэтому используйте его только когда геолокация действительно важна для краулимой страницы. Некоторые сайты (особенно Amazon) маршрутизируются через выделенные прокси независимо от переданной страны — для этих доменов разрешены все страны, даже если их нет в списке поддерживаемых ниже.
Вам доступны следующие страны:
| Австралия (AU) | Бразилия (BR) | Канада (CA) |
| Швейцария (CH) | Китай (CN) | Германия (DE) |
| Испания (ES) | Финляндия (FI) | Франция (FR) |
| Великобритания (GB) | Индия (IN) | Япония (JP) |
| Мексика (MX) | Нидерланды (NL) | Норвегия (NO) |
| Польша (PL) | Россия (RU) | Сейшелы (SC) |
| Швеция (SE) | Турция (TR) | Украина (UA) |
| США (US) |
Заголовки и cookies
Пробрасывайте свои заголовки запроса и cookies на целевой сайт или закрепите sticky-сессию, чтобы значения Set-Cookie из одного вызова воспроизводились в следующем. Полезно, когда цели нужен Accept-Language, CSRF-cookie или авторизованная сессия, которая должна сохраняться между запросами в потоке.
accept-language:en-GB|accept-encoding:gzip. Сочетайте с get_headers=true, чтобы также увидеть заголовки ответа целевого сайта.Cookie: key1=value1; key2=value2.Разрешённые заголовки. Не каждый заголовок, переданный через request_headers, дойдёт до целевого сайта — Crawlbase по умолчанию вырезает небольшой набор. Чтобы проверить, что действительно отправляется, отправьте тестовый запрос на https://postman-echo.com/headers и посмотрите, что получает echo-сервис. Если вам нужно авторизовать дополнительный заголовок для вашего токена, свяжитесь с поддержкой, указав имя заголовка(-ов).
JavaScript-рендеринг
Эти параметры требуют JavaScript token. Они управляют тем, как headless-браузер ждёт контент перед захватом DOM. Если вы хватаетесь сразу за несколько, порядок размышлений такой: сначала page_wait (фиксированная задержка для предсказуемых анимаций), затем ajax_wait (откажитесь от фиксированной задержки, если страница делает сетевые запросы после монтирования), затем scroll (только если нужный контент находится ниже сгиба), затем css_click_selector (только если кнопка или аккордеон скрывают данные).
Распространённая ошибка: задавать page_wait слишком большим «на всякий случай». Каждая лишняя миллисекунда — это конкурентность, которую вы не сможете использовать в другом месте. Начните с 0, увеличивайте только когда видите обрезанный вывод, и рассмотрите ajax_wait как более умную альтернативу — он возвращает результат, как только сеть переходит в простой, а не блокируется на фиксированном таймауте.
scroll=true.screenshot_url в заголовках ответа (или в JSON-теле при format=json) и истекает через час. Для мульти-снимков или полностраничных сценариев используйте отдельный Screenshots API.Опции вывода скриншота. При screenshot=true по умолчанию захватывается вся отрендеренная страница. Чтобы ограничиться только вьюпортом, добавьте mode=viewport; сочетайте с width и height (в пикселях), чтобы задать размер захвата. Оба значения по умолчанию равны размерам экрана и работают только при mode=viewport. Пример: &screenshot=true&mode=viewport&width=1200&height=800.
Как тарифицируется scroll. Запросы с прокруткой тарифицируются по общему времени серверной обработки. Первые 8 секунд (загрузка страницы + прокрутка вместе) считаются как 1 запрос; каждые дополнительные 5 секунд сверх этого добавляют ещё 1 тарифицируемый запрос. 20-секундная прокрутка = 1 (первые 8 с) + 1 (9–13 с) + 1 (14–18 с) + 1 (19–20 с, неполные блоки считаются полностью) = 4 тарифицируемых запроса. Если страница завершается до scroll_interval, тарифицируется только фактическое время обработки.
Максимальное значение scroll_interval — 60 секунд: после 60 с прокрутка останавливается и возвращается ответ. Когда вы задаёте scroll_interval=60, держите клиентское соединение открытым не менее 90 секунд, чтобы у ответа было время вернуться. Сочетание scroll с page_wait увеличивает общее время обработки и, соответственно, число тарифицируемых запросов.
Параметр css_click_selector срабатывает только при использовании JavaScript token (он выполняется внутри headless-браузера до захвата DOM). Принимает любой полностью указанный, валидный CSS-селектор — например ID вроде #some-button, класс вроде .some-other-button или селектор атрибута вроде [data-tab-item="tab1"]. Всегда URL-кодируйте значение, чтобы специальные символы прошли через query-строку без потерь.
Если селектор не найден на странице, запрос завершается ошибкой с pc_status 595. Чтобы всё же получить ответ, когда цель клика может отсутствовать, добавьте универсальный селектор как фолбэк через запятую. Например, #some-button,body кликает по body, когда #some-button не существует.
Несколько селекторов. Чтобы кликнуть по нескольким элементам подряд перед захватом, разделяйте их вертикальной чертой (|). URL-кодируйте всё значение целиком, включая черту. Например, клик по #start-button, а затем по .next-page-link в исходном виде выглядит как #start-button|.next-page-link, или %23start-button%7C.next-page-link в URL-кодированном виде. Клики выполняются в указанном порядке. Если какой-либо селектор в цепочке отсутствует, действует то же правило pc_status 595, поэтому шаблон фолбэка ,body работает для каждого селектора в отдельности.
Нужно выполнить пользовательский JavaScript внутри страницы перед тем, как Crawlbase захватит DOM (например, отправить синтетическое событие, изменить состояние, форсировать fetch)? Это функция, включаемая для конкретного аккаунта в зависимости от вашего сценария — свяжитесь с поддержкой, опишите задачу, и мы её настроим.
Async и хранилище
Async-режим переключает API с «блокируй, пока не получу страницу» на «поставь в очередь и сообщи, когда готово». Endpoint сразу возвращает rid; фактический результат доставляется на указанный вами webhook или сохраняется в Cloud Storage и забирается позже по тому же rid. Это правильный режим для пакетных задач и медленных целей — async освобождает слот конкурентности в момент постановки запроса в очередь, так что вы можете продолжать отправлять запросы, пока краулы ещё выполняются. Для больших объёмов (миллионы URL) используйте Enterprise Crawler, который стоит перед этим же async-конвейером и добавляет ретраи, управление частотой и доставку результатов.
Флаг async=true сейчас поддерживается только для URL linkedin.com. Если вам нужны async-краулы на других доменах, свяжитесь с поддержкой, указав целевой домен, чтобы мы включили это для вашего токена.
rid сразу, не блокируясь. Результат доставляется на callback, если он задан, или доступен через Cloud Storage по rid.async=true, если вы не хотите опрашивать вручную.rid в дополнение к телу.Формат вывода
Ответ по умолчанию — это сырое тело страницы, ровно то, что получил бы браузер после рендеринга и обхода анти-бот защиты. Для большинства конвейеров это правильная форма (ваш парсер дальше работает с HTML напрямую). Используйте format=json, когда хотите получить метаданные (статус, конечный URL, RID, заголовки) в одном envelope, а не разбросанные между заголовками ответа и телом. Используйте scraper= или autoparse=true, когда цель — это сайт, для которого у нас уже есть парсер: вы полностью пропускаете шаг парсинга и получаете чистые структурированные поля вместо сырой разметки.
html возвращает сырую страницу с метаданными в заголовках ответа. json упаковывает страницу и все метаданные в один JSON-объект. md конвертирует страницу в GitHub-Flavored Markdown — сочетайте с md_readability=true, чтобы предварительно убрать навигацию/сайдбар/рекламу.format=md. При true Crawlbase прогоняет страницу через readability-проход перед конвертацией в Markdown — отбрасывает «обвес» (навигация, сайдбар, футер, рекламные слоты) и оставляет основной текст статьи. Лучший вариант для превращения постов и статей в чистый контекст для LLM.format=json. Форматирует JSON-envelope с отступами и переносами строк для удобства чтения; в продакшене отключайте, чтобы ответы оставались компактными.amazon-product-details.Управление ответом
Эти параметры меняют то, что содержит ответ, или то, как Crawlbase решает, что запрос успешен. Используйте get_headers и get_cookies, когда вам нужны заголовки ответа целевого сайта или значения Set-Cookie (по умолчанию они вырезаются). Используйте custom_success_codes, когда цель легитимно возвращает не-2xx статус, который ваш конвейер должен трактовать как успешный фетч — без этого Crawlbase будет повторять такие ответы за вас.
original_header_* в заголовках ответа или сгруппированными под original_headers при format=json.Set-Cookie целевого сайта. Они возвращаются как original_set_cookie в заголовках ответа или под тем же ключом при format=json.custom_success_codes=403,429,503. Crawlbase не будет их повторять, а исходный статус сохраняется в original_status. Используйте, когда цель легитимно возвращает эти коды для вашего endpoint (API за авторизацией, страницы с региональной блокировкой, тело которых вам всё ещё нужно).POST-запросы
Используйте POST, когда целевой endpoint ожидает тело запроса — отправка форм, JSON API, GraphQL, всё, что не помещается в query string. Тот же endpoint, те же параметры, та же форма ответа, что и у GET; меняется только HTTP-метод и тело.
POST-запросы работают только с Normal token. JavaScript token (и параметры JS-рендеринга page_wait, ajax_wait, scroll, css_click_selector) доступны только для GET — когда вам нужно отправить форму на странице с JS-рендерингом, используйте JavaScript token с css_click_selector, чтобы кликнуть на кнопку формы, вместо того чтобы делать POST на URL формы напрямую.
По умолчанию Content-Type — application/x-www-form-urlencoded. Передавайте поля формы как тело запроса — Crawlbase пересылает их цели без изменений.
curl 'https://api.crawlbase.com/?token=YOUR_TOKEN' \
--data-urlencode 'url=https://postman-echo.com/post' -G \
-F 'parameter1=testing some post data' \
-F 'parameter2=here goes some data'import requests
from urllib.parse import quote_plus
url = quote_plus('https://postman-echo.com/post')
res = requests.post(
f'https://api.crawlbase.com/?token=YOUR_TOKEN&url={url}',
data={'parameter1': 'value', 'parameter2': 'another value'},
)
print(res.status_code, res.text)const url = encodeURIComponent('https://postman-echo.com/post');
const body = new URLSearchParams({ parameter1: 'value', parameter2: 'another' });
const res = await fetch(`https://api.crawlbase.com/?token=YOUR_TOKEN&url=${url}`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body,
});
console.log(res.status, await res.text());require 'net/http'
uri = URI('https://api.crawlbase.com')
uri.query = URI.encode_www_form(token: 'YOUR_TOKEN', url: 'https://postman-echo.com/post')
res = Net::HTTP.post_form(uri, 'parameter1' => 'value', 'parameter2' => 'another')
puts res.code, res.body<?php
$url = 'https://postman-echo.com/post';
$body = http_build_query(['parameter1' => 'value', 'parameter2' => 'another']);
$ch = curl_init('https://api.crawlbase.com/?token=YOUR_TOKEN&url=' . urlencode($url));
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
echo curl_exec($ch);package main
import (
"fmt"
"io"
"net/http"
"net/url"
"strings"
)
func main() {
target := url.QueryEscape("https://postman-echo.com/post")
body := strings.NewReader("parameter1=value¶meter2=another")
res, _ := http.Post(
"https://api.crawlbase.com/?token=YOUR_TOKEN&url="+target,
"application/x-www-form-urlencoded",
body,
)
out, _ := io.ReadAll(res.Body)
fmt.Println(string(out))
}POST нельзя использовать для спама или иного вреда целевым сайтам. Crawlbase активно отслеживает паттерны злоупотреблений; аккаунты, замеченные в использовании POST для спама, credential stuffing или другого вредоносного трафика, будут заблокированы и сообщены.
POST с JSON-телом
Переопределите Content-Type form-urlencoded по умолчанию через post_content_type. URL-кодируйте значение (например, application/json становится application%2Fjson). Тело пересылается цели без изменений — кодируйте его как JSON самостоятельно.
curl 'https://api.crawlbase.com/?token=YOUR_TOKEN' \
--data-urlencode 'url=https://postman-echo.com/post' \
--data-urlencode 'post_content_type=application/json;charset=UTF-8' -G \
--request POST \
--data '{"param1":"value","param2":"another"}'import json, requests
from urllib.parse import quote_plus
url = quote_plus('https://postman-echo.com/post')
res = requests.post(
f'https://api.crawlbase.com/?token=YOUR_TOKEN'
f'&url={url}'
f'&post_content_type=application/json',
data=json.dumps({'param1': 'value', 'param2': 'another'}),
headers={'Content-Type': 'application/json'},
)
print(res.status_code, res.text)const url = encodeURIComponent('https://postman-echo.com/post');
const ct = encodeURIComponent('application/json;charset=UTF-8');
const body = JSON.stringify({ param1: 'value', param2: 'another' });
const res = await fetch(
`https://api.crawlbase.com/?token=YOUR_TOKEN&url=${url}&post_content_type=${ct}`,
{ method: 'POST', headers: { 'Content-Type': 'application/json' }, body },
);
console.log(res.status, await res.text());require 'net/http'
require 'json'
uri = URI('https://api.crawlbase.com')
uri.query = URI.encode_www_form(
token: 'YOUR_TOKEN',
url: 'https://postman-echo.com/post',
post_content_type: 'application/json'
)
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
req.body = { param1: 'value', param2: 'another' }.to_json
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.code, res.body<?php
$url = 'https://postman-echo.com/post';
$ct = urlencode('application/json;charset=UTF-8');
$body = json_encode(['param1' => 'value', 'param2' => 'another']);
$ch = curl_init(
'https://api.crawlbase.com/?token=YOUR_TOKEN'
. '&url=' . urlencode($url)
. '&post_content_type=' . $ct
);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
echo curl_exec($ch);package main
import (
"bytes"
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
target := url.QueryEscape("https://postman-echo.com/post")
ct := url.QueryEscape("application/json;charset=UTF-8")
body := bytes.NewBufferString(`{"param1":"value","param2":"another"}`)
res, _ := http.Post(
"https://api.crawlbase.com/?token=YOUR_TOKEN&url="+target+"&post_content_type="+ct,
"application/json",
body,
)
out, _ := io.ReadAll(res.Body)
fmt.Println(string(out))
}Примечание: целевой сайт сам решает, принимать ли тело. Crawlbase пересылает запрос честно — если цель возвращает 4xx из-за неправильной формы тела, это отразится в original_status, а не в pc_status. Смотрите Ошибки для паттерна ветвления.
PUT-запросы
PUT работает так же, как POST — тот же endpoint, те же параметры, те же правила кодирования тела. Единственное отличие — HTTP-метод.
curl 'https://api.crawlbase.com/?token=YOUR_TOKEN' \
--data-urlencode 'url=https://api.example.com/resource/42' -G \
--request PUT \
--header 'Content-Type: application/json' \
--data '{"name":"updated","status":"active"}'import requests
from urllib.parse import quote_plus
url = quote_plus('https://api.example.com/resource/42')
res = requests.put(
f'https://api.crawlbase.com/?token=YOUR_TOKEN&url={url}&post_content_type=application/json',
data='{"name":"updated","status":"active"}',
headers={'Content-Type': 'application/json'},
)
print(res.status_code, res.text)const url = encodeURIComponent('https://api.example.com/resource/42');
const ct = encodeURIComponent('application/json');
const body = JSON.stringify({ name: 'updated', status: 'active' });
const res = await fetch(
`https://api.crawlbase.com/?token=YOUR_TOKEN&url=${url}&post_content_type=${ct}`,
{ method: 'PUT', headers: { 'Content-Type': 'application/json' }, body },
);
console.log(res.status, await res.text());require 'net/http'
require 'json'
uri = URI('https://api.crawlbase.com')
uri.query = URI.encode_www_form(
token: 'YOUR_TOKEN',
url: 'https://api.example.com/resource/42',
post_content_type: 'application/json'
)
req = Net::HTTP::Put.new(uri, 'Content-Type' => 'application/json')
req.body = { name: 'updated', status: 'active' }.to_json
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }
puts res.code, res.body<?php
$url = 'https://api.example.com/resource/42';
$ct = urlencode('application/json');
$body = json_encode(['name' => 'updated', 'status' => 'active']);
$ch = curl_init(
'https://api.crawlbase.com/?token=YOUR_TOKEN'
. '&url=' . urlencode($url)
. '&post_content_type=' . $ct
);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
echo curl_exec($ch);package main
import (
"bytes"
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
target := url.QueryEscape("https://api.example.com/resource/42")
ct := url.QueryEscape("application/json")
body := bytes.NewBufferString(`{"name":"updated","status":"active"}`)
req, _ := http.NewRequest(
"PUT",
"https://api.crawlbase.com/?token=YOUR_TOKEN&url="+target+"&post_content_type="+ct,
body,
)
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
out, _ := io.ReadAll(res.Body)
fmt.Println(string(out))
}Как и POST, PUT требует Normal token. Используйте post_content_type для управления media-типом тела, если оно не form-urlencoded.
Crawlbase активно отслеживает POST- и PUT-трафик. Отправка тел запросов на сторонние сайты, которыми вы не владеете — спам в комментариях, мошеннические отправки форм, скриптовая регистрация аккаунтов — приводит к блокировке исходного аккаунта при первом обнаружении. Используйте эти методы для легитимных интеграций с API, ваших собственных staging- и production-эндпоинтов и явно разрешённой автоматизации.
Ответ
Успешные ответы возвращают целевую страницу в теле. Метаданные находятся в заголовках ответа.
Заголовки
| Заголовок | Описание |
|---|---|
pc_status | Статус-код Crawlbase. 200 = успех. |
original_status | HTTP-статус с целевого сайта. |
url | Финальный URL после редиректов. |
rid | ID запроса. Возвращается при async=true или store=true. |
content-type | MIME-тип тела (text/html, application/json, image/png и т. д.). |
original_header_* | Возвращается при get_headers=true. Каждый заголовок с целевого сайта приходит с префиксом original_header_ (например, original_header_x_frame_options). Группируется под original_headers при format=json. |
screenshot_url | Возвращается при screenshot=true. Временный URL JPEG для отрендеренной страницы; истекает через час после краулинга. |
original_set_cookie | Возвращается при get_cookies=true. Сконкатенированные значения Set-Cookie из ответа целевого сайта. |
domain_complexityтакже X-Domain-Complexity | Уровень сложности краулимого домена — один из standard, moderate или complex. Отражает ресурсы, необходимые для обхода защиты сайта, и напрямую соответствует ценовому уровню, по которому тарифицируется запрос. Смотрите уровни сложности ниже. |
storage_url | Возвращается, когда запрос был сделан с store=true. Указатель на сохранённую копию ответа в Crawlbase Cloud Storage; используйте вместе с rid, чтобы получить её позже. |
Content-Type | text/markdown; charset=utf-8, когда запрос был сделан с format=md; стандартные text/html или application/json в остальных случаях. |
X-Markdown-Flavor | Диалект markdown тела ответа — в настоящее время GitHub Flavored Markdown (GFM). Возвращается только при format=md. |
X-Markdown-Features | Список используемых в теле возможностей GFM через запятую (например, tables,lists). Позволяет выбрать парсер с включёнными нужными расширениями. Возвращается только при format=md. |
X-Markdown-Base-URL | Хост разрешённого URL (после всех редиректов). Полезно для разрешения относительных ссылок в markdown-теле. Возвращается только при format=md. |
X-Markdown-Generator | Идентифицирует конвертер — значение ProxyCrawl-API. Возвращается только при format=md. |
HTML-ответ
По умолчанию. format=html (или вообще без format) возвращает сырое тело страницы в HTTP body, а метаданные — в заголовках ответа (url, original_status, pc_status, X-Domain-Complexity и любые записи original_header_*, на которые вы подписались через get_headers=true).
GET 'https://api.crawlbase.com/?token=YOUR_TOKEN&url=https%3A%2F%2Fgithub.com%2Fcrawlbase&format=html'
Response:
Headers:
url: https://github.com/crawlbase
original_status: 200
pc_status: 200
X-Domain-Complexity: standard
Body:
<!doctype html><html>
<head>...</head>
<body>... (full page HTML) ...</body>
</html>JSON-ответ
Установите format=json, чтобы получить те же данные в виде единого JSON-объекта:
GET 'https://api.crawlbase.com/?token=YOUR_TOKEN&url=https%3A%2F%2Fgithub.com%2Fcrawlbase&format=json'
Response:
{
"original_status": 200,
"pc_status": 200,
"url": "https://github.com/crawlbase",
"domain_complexity": "standard",
"body": "<!doctype html><html>... (full page HTML) ...</html>"
}Markdown-ответ
format=md возвращает страницу, уже сконвертированную в GitHub Flavored Markdown, в теле, с Content-Type: text/markdown; charset=utf-8 и блоком метаданных-заголовков X-Markdown-* (Flavor, Features, Base-URL, Generator) наряду с обычными url / original_status / pc_status. Сочетайте с md_readability=true, когда хотите извлечь основной контент (тело статьи, без обвязки) перед запуском конвертации — смотрите параметр md_readability.
GET 'https://api.crawlbase.com/?token=YOUR_TOKEN&url=https%3A%2F%2Fgithub.com%2Fcrawlbase&format=md'
Response:
Headers:
Content-Type: text/markdown; charset=utf-8
X-Markdown-Flavor: GitHub Flavored Markdown (GFM)
X-Markdown-Features: tables,lists
X-Markdown-Base-URL: github.com
X-Markdown-Generator: ProxyCrawl-API
url: https://github.com/crawlbase
original_status: 200
pc_status: 200
Body:
# crawlbase
... (markdown text of the page) ...Тарифицируемые запросы
Crawlbase тарифицирует только те запросы, у которых pc_status равен 200 и original_status — один из:
| Код | Значение |
|---|---|
200 | OK |
201 | Created |
204 | No Content |
301 | Moved Permanently |
302 | Found — только когда редирект был выполнен и вернул контент |
404 | Not Found |
410 | Gone |
Любой другой original_status бесплатен, как и любой pc_status, отличный от 200. Используйте этот список при сверке счёта за использование с логами вашего приложения.
Уровни сложности домена
Поле domain_complexity (также возвращаемое как заголовок ответа X-Domain-Complexity) сообщает, насколько сложно было краулить целевой домен — и в какой ценовой уровень попал запрос.
standard— лёгкий для краулинга, минимальная защита. Самый низкий ценовой уровень.moderate— умеренная анти-бот защита, требующая специализированной обработки. Средний ценовой уровень.complex— продвинутая защита, требующая специализированных ресурсов. Самый высокий ценовой уровень.
По ценам конкретных уровней смотрите ваш тарифный план или свяжитесь с отделом продаж.
Распространённые шаблоны
JS-рендеринг SPA с прокруткой
curl 'https://api.crawlbase.com/?token=JS_TOKEN' \
--data-urlencode 'url=https://feed.example.com' \
--data-urlencode 'page_wait=2000' \
--data-urlencode 'scroll=true' \
--data-urlencode 'scroll_interval=15' -Gfrom crawlbase import CrawlingAPI
api = CrawlingAPI({'token': 'JS_TOKEN'})
res = api.get('https://feed.example.com', {
'page_wait': 2000,
'scroll': True,
'scroll_interval': 15,
})const { CrawlingAPI } = require('crawlbase');
const api = new CrawlingAPI({ token: 'JS_TOKEN' });
const res = await api.get('https://feed.example.com', {
page_wait: 2000,
scroll: true,
scroll_interval: 15,
});
console.log(res.body);require 'crawlbase'
api = Crawlbase::API.new(token: 'JS_TOKEN')
res = api.get('https://feed.example.com',
page_wait: 2000,
scroll: true,
scroll_interval: 15
)
puts res.body<?php
use Crawlbase\CrawlingAPI;
$api = new CrawlingAPI(['token' => 'JS_TOKEN']);
$res = $api->get('https://feed.example.com', [
'page_wait' => 2000,
'scroll' => true,
'scroll_interval' => 15,
]);
echo $res->body;package main
import (
"fmt"
"log"
"github.com/crawlbase/crawlbase-go"
)
func main() {
api, err := crawlbase.NewCrawlingAPI("JS_TOKEN")
if err != nil {
log.Fatal(err)
}
res, _ := api.Get("https://feed.example.com", map[string]string{
"page_wait": "2000",
"scroll": "true",
"scroll_interval": "15",
})
fmt.Println(res.Body)
}Гео-маршрутизированный запрос
# Get the German version of a localized site
curl 'https://api.crawlbase.com/?token=YOUR_TOKEN' \
--data-urlencode 'url=https://www.amazon.com/dp/B08N5WRWNW' \
--data-urlencode 'country=DE' -Gfrom crawlbase import CrawlingAPI
api = CrawlingAPI({'token': 'YOUR_TOKEN'})
res = api.get('https://www.amazon.com/dp/B08N5WRWNW', {'country': 'DE'})
print(res['body'])const { CrawlingAPI } = require('crawlbase');
const api = new CrawlingAPI({ token: 'YOUR_TOKEN' });
const res = await api.get('https://www.amazon.com/dp/B08N5WRWNW', { country: 'DE' });
console.log(res.body);require 'crawlbase'
api = Crawlbase::API.new(token: 'YOUR_TOKEN')
res = api.get('https://www.amazon.com/dp/B08N5WRWNW', country: 'DE')
puts res.body<?php
use Crawlbase\CrawlingAPI;
$api = new CrawlingAPI(['token' => 'YOUR_TOKEN']);
$res = $api->get('https://www.amazon.com/dp/B08N5WRWNW', ['country' => 'DE']);
echo $res->body;package main
import (
"fmt"
"log"
"github.com/crawlbase/crawlbase-go"
)
func main() {
api, err := crawlbase.NewCrawlingAPI("YOUR_TOKEN")
if err != nil {
log.Fatal(err)
}
res, _ := api.Get("https://www.amazon.com/dp/B08N5WRWNW", map[string]string{
"country": "DE",
})
fmt.Println(res.Body)
}Асинхронный краулинг с webhook
curl 'https://api.crawlbase.com/?token=YOUR_TOKEN' \
--data-urlencode 'url=https://example.com' \
--data-urlencode 'async=true' \
--data-urlencode 'callback=https://your-app.com/webhook' -G
# → returns immediately: { "rid": "a1B2c3D4e5F6" }
# → result POSTed to your callback when readyfrom crawlbase import CrawlingAPI
api = CrawlingAPI({'token': 'YOUR_TOKEN'})
res = api.get('https://example.com', {
'async': 'true',
'callback': 'https://your-app.com/webhook',
})
print(res['rid']) # → returned immediately; result POSTed to callback laterconst { CrawlingAPI } = require('crawlbase');
const api = new CrawlingAPI({ token: 'YOUR_TOKEN' });
const res = await api.get('https://example.com', {
async: true,
callback: 'https://your-app.com/webhook',
});
console.log(res.rid); // → returned immediately; result POSTed to callback laterrequire 'crawlbase'
api = Crawlbase::API.new(token: 'YOUR_TOKEN')
res = api.get('https://example.com',
async: true,
callback: 'https://your-app.com/webhook'
)
puts res.rid # → returned immediately; result POSTed to callback later<?php
use Crawlbase\CrawlingAPI;
$api = new CrawlingAPI(['token' => 'YOUR_TOKEN']);
$res = $api->get('https://example.com', [
'async' => 'true',
'callback' => 'https://your-app.com/webhook',
]);
echo $res->rid; // → returned immediately; result POSTed to callback laterpackage main
import (
"fmt"
"log"
"github.com/crawlbase/crawlbase-go"
)
func main() {
api, err := crawlbase.NewCrawlingAPI("YOUR_TOKEN")
if err != nil {
log.Fatal(err)
}
res, _ := api.Get("https://example.com", map[string]string{
"async": "true",
"callback": "https://your-app.com/webhook",
})
fmt.Println(res.RID) // → returned immediately; result POSTed to callback later
}Async освобождает ваш слот конкурентности в момент постановки запроса в очередь, поэтому долгий краулинг не съедает бюджет. Используйте его для медленных целей (тяжёлый JS, длинный page_wait), когда нужно прокачать большой объём.
Режим прокси
Тот же Crawling API можно вызывать как HTTP/HTTPS-прокси вместо REST-эндпоинта — это удобно, когда у вас уже есть скрейпер, скрипт автоматизации браузера или HTTP-клиент с поддержкой настройки прокси и вы предпочитаете поставить Crawlbase перед ним, а не переписывать слой запросов.
Направьте ваш клиент на smartproxy.crawlbase.com:8001 (HTTPS, рекомендуется) или smartproxy.crawlbase.com:8000 (HTTP) и передайте ваш token как имя пользователя прокси. Все возможности Crawling API — JS-рендеринг, обход анти-бот защиты, гео-маршрутизация — работают одинаково; отличается только форма запроса.
Режим прокси vs. Smart AI Proxy
Два продукта используют один и тот же hostname, но разные порты — их легко перепутать. Возможности по сути одинаковые в обоих (маршрутизация по странам, эмуляция устройств, сессии, кастомные заголовки, JS-рендеринг через управляющие CrawlbaseAPI-* заголовки); они различаются подпиской, по которой выставляется счёт, и тарифом по конкурентности / потокам, который эта подписка предоставляет:
- Crawling API в режиме прокси (этот раздел) → порты
8000/8001. Маршрутизация через ваш план Crawling API: та же месячная квота, тот же бюджет конкурентности, та же тарификация за успешные запросы, что и в REST-режиме. Выбирайте этот вариант, если уже оплачиваете Crawling API и хотите получить интерфейс в форме прокси наряду с REST-эндпоинтом. - Smart AI Proxy (отдельный продукт, см. Smart Proxy) → порты
8012/8013. Отдельный SKU со своей подпиской и собственной моделью потоков / конкурентности, рассчитанный на скрейпинговые пайплайны с приоритетом прокси, в которых уже работает большое количество потоков. Под капотом — та же сеть и те же управляющие заголовки; выбор сводится к тому, какой контракт и форма конкурентности подходят под ваше использование.
Правило большого пальца: выбирайте продукт, подписка на который у вас уже есть (или чья модель ценообразования соответствует форме вашего трафика). Поверхность возможностей одинакова; порты просто маршрутизируют вас в нужную полосу биллинга и конкурентности.
Быстрый старт
Первый вызов из вашего шелла — Normal token, HTTPS-прокси:
# HTTPS proxy (recommended)
curl -x 'https://[email protected]:8001' \
-k 'https://httpbin.org/ip'
# HTTP alternative
curl -x 'http://[email protected]:8000' \
-k 'https://httpbin.org/ip'import requests
proxies = {
'http': 'http://[email protected]:8000',
'https': 'http://[email protected]:8000',
}
res = requests.get('https://httpbin.org/ip', proxies=proxies, verify=False)
print(res.status_code, res.text)const { HttpsProxyAgent } = require('https-proxy-agent');
const agent = new HttpsProxyAgent('http://[email protected]:8000');
const res = await fetch('https://httpbin.org/ip', { agent });
console.log(res.status, await res.text());require 'net/http'
uri = URI('https://httpbin.org/ip')
http = Net::HTTP.new(uri.host, uri.port,
'smartproxy.crawlbase.com', 8000, 'YOUR_TOKEN', '')
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
res = http.get(uri.request_uri)
puts res.code, res.body<?php
$ch = curl_init('https://httpbin.org/ip');
curl_setopt($ch, CURLOPT_PROXY, 'smartproxy.crawlbase.com:8000');
curl_setopt($ch, CURLOPT_PROXYUSERPWD, 'YOUR_TOKEN:');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
echo curl_exec($ch);package main
import (
"crypto/tls"
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
proxyURL, _ := url.Parse("http://[email protected]:8000")
client := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(proxyURL),
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}
res, _ := client.Get("https://httpbin.org/ip")
out, _ := io.ReadAll(res.Body)
fmt.Println(string(out))
}Для целей с JS-рендерингом подставьте ваш JavaScript token:
curl -x 'https://[email protected]:8001' \
-k 'https://spa.example.com'import requests
proxies = {
'http': 'http://[email protected]:8000',
'https': 'http://[email protected]:8000',
}
res = requests.get('https://spa.example.com', proxies=proxies, verify=False)
print(res.status_code)const { HttpsProxyAgent } = require('https-proxy-agent');
const agent = new HttpsProxyAgent('http://[email protected]:8000');
const res = await fetch('https://spa.example.com', { agent });
console.log(res.status);require 'net/http'
uri = URI('https://spa.example.com')
http = Net::HTTP.new(uri.host, uri.port,
'smartproxy.crawlbase.com', 8000, 'YOUR_JS_TOKEN', '')
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
res = http.get(uri.request_uri)
puts res.code<?php
$ch = curl_init('https://spa.example.com');
curl_setopt($ch, CURLOPT_PROXY, 'smartproxy.crawlbase.com:8000');
curl_setopt($ch, CURLOPT_PROXYUSERPWD, 'YOUR_JS_TOKEN:');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch);package main
import (
"crypto/tls"
"fmt"
"net/http"
"net/url"
)
func main() {
proxyURL, _ := url.Parse("http://[email protected]:8000")
client := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(proxyURL),
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}
res, _ := client.Get("https://spa.example.com")
fmt.Println(res.Status)
}Ограничения по частоте
Лимит по умолчанию в режиме прокси — 20 запросов в секунду (~1,7 млн запросов в день). Клиентам, опирающимся на конкурентность, стоит мыслить в потоках, а не в RPS — при типичной задержке Crawling API (~4 с для страницы товара Amazon) это превращается примерно в 80 одновременных потоков. На более быстрых целях получается меньше потоков.
Если упёрлись в лимит, напишите в поддержку с описанием вашего сценария, чтобы согласовать более высокую конкурентность.
Ошибки и повторные попытки
Crawling API выдаёт два кода статуса в каждом ответе: original_status (что вернул целевой сайт) и pc_status (как Crawlbase интерпретировал это после применения правил анти-бот, редиректов и валидации контента). Они могут расходиться — цель может вернуть 200 с пустым телом, и тогда original_status будет 200, а pc_status — 520. При принятии решения о повторе всегда ориентируйтесь на pc_status.
Самые распространённые сбои, специфичные для Crawling API:
| Код | Значение | Действие |
|---|---|---|
422 | url отсутствует или не URL-кодирован | Закодируйте URL перед отправкой. Большинство клиентов (libcurl --data-urlencode, Python requests, Node fetch) делают это автоматически — но в query-строках, собранных вручную, это часто упускают. |
520 | Пустой ответ от целевого сайта | Повторите один раз. Если по-прежнему пусто, переключитесь с Normal на JS token — многие сайты отдают пустой каркас не-браузерным user agent'ам и полагаются на JS для заполнения. |
521 | Целевой сайт недоступен / лежит | Считайте это временной upstream-ошибкой. Backoff + повтор; если ситуация сохраняется минутами, сайт действительно лежит. |
522 | Тайм-аут соединения с целевым сайтом | Повторите с backoff. Попробуйте другую country, если цель чувствительна к географии. |
523 | Origin недоступен с выбранной точки выхода | Повторите без country (пусть авто-маршрутизация выберет сама) или с другой страной. |
525 | Не удалось решить анти-бот челлендж | Переключитесь с Normal на JS token. Если уже на JS — повторите; если ситуация сохраняется, эскалируйте в поддержку — обычно это означает, что цель выкатила новый вариант челленджа. |
595 | Селектор не найден. Страница загрузилась успешно, но CSS-селектор, переданный через css_click_selector, не совпал ни с одним элементом. | Добавьте к селектору запасной вариант (#start-button,body), чтобы клик всё равно попадал по известному элементу. Полный шаблон описан в заметках про css_click_selector. |
599 | Внутренняя ошибка Crawlbase | Повторите. Если запрос стабильно упирается в эту ошибку, обратитесь в поддержку с указанием rid. |
Полный справочник по HTTP- и pc_status-кодам — в Status Codes; в Error handling описан рекомендуемый цикл retry-with-backoff и SDK-хелперы, реализующие его за вас на каждом языке.
Пример якорения. Самая частая причина расхождения pc_status и original_status — CAPTCHA: целевой сайт возвращает 200 (страница капчи отрисовалась нормально), но Crawlbase распознаёт ответ как промежуточную страницу и выдаёт pc_status: 503, чтобы вы могли обойти её, а не воспринимать HTML капчи как свои данные.
Нестандартные коды pc_status. Коды вне обычного диапазона HTTP — 601, 999 и подобные — это внутренние маркеры, используемые инженерной командой Crawlbase. Они появляются в ответе только для того, чтобы помочь отладке при обращении в поддержку; обрабатывать их в коде приложения не нужно.
Стратегия повторных попыток
Простая версия: повторяйте временные ошибки (5xx) с экспоненциальным backoff до некоторого предела (обычно 3–5 попыток), не повторяйте клиентские ошибки (4xx — они сами не исправятся) и один раз переключайте тип токена при первом 520/525, прежде чем продолжать повторы. SDK-хелперы реализуют этот цикл с разумными значениями по умолчанию; для кастомного клиента правило большого пальца такое:
- Первый повтор: ~1 с после сбоя
- Второй повтор: ~3 с после сбоя
- Третий повтор: ~10 с после сбоя
- После этого: лог + алерт; устойчивые сбои обычно означают изменение на стороне цели, а не временные сетевые проблемы
Все повторы к этому API бесплатны — в квоту засчитываются только успешные ответы (pc_status: 200). Это делает агрессивный backoff дешёвым; единственная реальная цена повторов — задержка, которую вы добавляете в свой пайплайн.
Производительность и лучшие практики
Несколько шаблонов повторяются у клиентов, использующих этот API в масштабе. Применение их с самого начала позволяет избежать самых частых категорий тикетов в поддержку.
- Используйте самый дешёвый токен, который работает. Не выбирайте JavaScript token «на всякий случай» — запросы с Normal token быстрее и используют меньше конкурентности. Переходите на JS только когда ответ Normal пуст или заблокирован челленджем.
- Предпочитайте
ajax_waitвместоpage_wait. Фиксированные задержки расходуют конкурентность на каждом запросе, даже на быстрых.ajax_waitвозвращает результат в момент, когда сеть страницы переходит в idle — обычно быстрее в среднем и медленнее только на действительно долго загружающихся страницах. - Прокачивайте большие объёмы через async + webhook. Синхронный режим — правильный выбор по умолчанию для разовых и интерактивных сценариев. Для пакетных задач от нескольких сотен URL и больше асинхронный режим (или Enterprise Crawler) сохраняет ваш бюджет конкурентности свободным для новых отправок, пока существующие краулинги завершаются.
- Переиспользуйте сессии для stateful-сценариев. Если целевой сайт требует авторизованной сессии или cookies корзины, удерживайте session ID и передавайте его в последующих запросах, чтобы переиспользовался тот же выходной IP и тот же набор cookies. См. Authentication для шаблона работы с session-cookie.
- Следите за заголовком
remaining. Делайте backoff заранее, до того как упрётесь в лимит конкурентности, а не узнавайте о нём через 429 — ответ содержит число оставшихся слотов, поэтому здоровый клиент проактивно делает паузу вместо того, чтобы реагировать на ошибки.

