Docs
Войти

Как это работает

Каждый запрос Crawling API принимает целевой URL и возвращает страницу, которую этот целевой сайт отдал бы реальному браузеру в нужной географии, с нужным профилем устройства, после того как любые анти-бот челленджи будут разрешены. На каждом вызове последовательно происходят три вещи:

  1. Маршрутизация. Запрос отправляется через резидентный или дата-центровый exit-узел - автоматически по умолчанию или в конкретной стране, если вы передаёте country=. Доступны sticky-сессии, чтобы последовательность вызовов использовала тот же IP.
  2. Рендеринг. Если вы аутентифицируетесь через JavaScript token, URL загружается в реальном headless-браузере. Контролы page-wait, scroll, click и AJAX-idle позволяют дождаться фактического контента, а не начальной HTML-оболочки.
  3. Обход анти-бот защиты. 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-токеном возвращает пустой body или 525 (челлендж не удалось решить), стандартное решение - повторить запрос на JavaScript-токене. Большинству современных целей нужен браузер, даже если их начальный HTML выглядит полным. Полный flow управления токенами см. в Authentication.

Параллельность и тарификация

Каждый запрос, возвращающий pc_status: 200, засчитывается в вашу ежемесячную квоту. Неуспешные запросы (таймауты, блокировки, 5xx от целевого сайта) бесплатны - повторные попытки против нестабильного upstream не удивят ваш счёт. Лимиты параллельности масштабируются с вашим планом; ответ включает заголовок remaining, который можно использовать для проактивного отката до достижения лимита. Долгие краулы (тяжёлый JS-рендеринг, большой page_wait) должны использовать async-режим ниже, чтобы освобождать слот параллельности в момент постановки запроса в очередь.

Клиентские таймауты. Среднее время ответа — 4–10 секунд на запрос, но запросы из «хвоста» латентности (тяжёлые SPA, scroll_interval=60, медленные источники) могут занять больше. Установите клиентский таймаут как минимум на 90 секунд, чтобы легитимные медленные ответы не отваливались по таймауту до их прихода.

Прочие рекомендации на стороне клиента. Отправляйте Accept-Encoding: gzip на каждом запросе - payload-ы нетривиальны (полные HTML-страницы или markdown), и gzip обычно уменьшает их до трети сетевого размера. Если вы используете Scrapy, отключите DNS-кэш, чтобы API-хост оставался разрешимым на протяжении долгоживущих краулов.

Endpoint

GETPOSTPUThttps://api.crawlbase.com/?token=YOUR_TOKEN&url=ENCODED_URL
  • Методы: GET (только query), POST (form или JSON body), PUT (сырой payload).
  • Параметр url должен быть полностью URL-кодированным.
  • Body возвращается как содержимое целевой страницы (HTML, JSON, изображение и т. д.).
  • Метаданные возвращаются в виде заголовков ответа (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: передавайте query-параметры ниже, чтобы управлять рендерингом, гео, форматом вывода и async-поведением. Используйте POST, когда нужно отправить форму или JSON-body, и PUT для загрузки сырых payload-ов.

Параметры запроса

Все параметры передаются как значения строки запроса. Обязательны только token и url.

Обязательные

token
stringобязательный
Ваш Normal или JavaScript токен. См. Authentication.
url
stringобязательный
Целевой URL, полностью URL-кодированный. Должен включать схему (http:// или https://).

Маршрутизация и гео

Выберите, откуда исходит запрос и какое устройство видит целевой сайт. Маршрутизация важна для интернет-магазинов, SERP и любого сайта, который локализует контент по IP - немецкий каталог Amazon недоступен из US-exit даже с правильным URL, а Google SERP локализуются и по географии, и по URL-параметрам hl/gl в сочетании с IP. Установите country явно, и правильная валюта, язык и ассортимент появятся автоматически.

country
stringопциональный
Двухбуквенный ISO-код страны (US, GB, DE, JP, …) для маршрутизации краула через exit-узлы этой страны. По умолчанию — автоматический выбор гео.
device
desktop | tablet | mobiledesktop
Эмулирует User-Agent и viewport выбранного класса устройств.
user_agent
stringопциональный
Переопределить заголовок User-Agent. Используйте умеренно - значения по умолчанию настроены под каждую цель.
tor_network
booleanfalse
Маршрутизировать запрос через сеть Tor, чтобы можно было краулить сайты .onion. Не используйте для clearnet-целей - Tor-exit-узлы медленнее и более «шумные», чем резидентный пул.
Country может быть автоматически переопределён

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 или авторизованная сессия, которая должна сохраняться между запросами в потоке.

request_headers
stringопциональный
URL-кодированный список заголовков для проброса, разделённых вертикальной чертой: accept-language:en-GB|accept-encoding:gzip. Сочетайте с get_headers=true, чтобы также увидеть заголовки ответа целевого сайта.
set_cookies
stringопциональный
Cookies для проброса на целевой сайт в стандартном формате заголовка Cookie: key1=value1; key2=value2.
cookies_session
stringопциональный
Sticky cookie-сессия - Crawlbase воспроизводит cookies, возвращённые с предыдущих вызовов, на каждом последующем вызове с тем же значением. Любая строка до 32 символов; новое значение начинает новую сессию. Сессии истекают через 300 секунд после последнего вызова.

Разрешённые заголовки. Не каждый заголовок, который вы передаёте через 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 как более умную альтернативу: он возвращает результат, как только сеть переходит в idle, а не блокируется на фиксированном таймауте.

page_wait
int (мс)0
Подождать столько миллисекунд после загрузки страницы перед захватом. Полезно для контента, появляющегося с анимацией.
ajax_wait
booleanfalse
Ждать, пока сеть не перейдёт в простой (нет запросов ~500 мс). Лучший вариант для SPA, подгружающих данные после монтирования.
css_click_selector
stringопциональный
CSS-селектор - Crawlbase кликнет по соответствующему элементу перед захватом. URL-кодируйте специальные символы.
scroll
booleanfalse
Прокрутить страницу до конца перед захватом. Запускает lazy-load.
scroll_interval
int (с)10
Максимальное число секунд, отведённое на прокрутку. Используется в сочетании с scroll=true.
screenshot
booleanfalse
Сделать JPEG-снимок отрендеренной страницы. URL возвращается как 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 string в целости.

Если селектор не найден на странице, запрос завершается с pc_status 595. Чтобы всё же получить ответ, когда цель клика может отсутствовать, добавьте универсально находимый селектор как fallback - через запятую. Например, #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 (например, диспатчить синтетическое событие, изменить state, форсировать fetch)? Это per-account фича, привязанная к вашему use case: свяжитесь с поддержкой с описанием того, что вы пытаетесь сделать, и мы это настроим.

Async и хранилище

Async-режим переключает API с «блокировать, пока у меня не будет страницы» на «поставь в очередь и сообщи, когда готово». Endpoint возвращается немедленно с rid; фактический результат доставляется на указанный вами webhook или сохраняется в Cloud Storage и забирается позже по тому же rid. Это правильный режим для пакетных задач и медленных целей: async освобождает ваш слот параллельности в момент постановки запроса в очередь, так что вы можете продолжать отправлять запросы, пока краулы ещё выполняются. Для высокообъёмных задач (миллионы URL) используйте Enterprise Crawler, который стоит перед тем же async-пайплайном с retry-логикой, управлением частотой и доставкой результатов.

Async-режим сейчас работает только для linkedin.com

Флаг async=true сейчас поддерживается только для URL linkedin.com. Если вам нужны async-краулы на других доменах, свяжитесь с поддержкой, указав целевой домен, чтобы мы включили это для вашего токена.

async
booleanfalse
Возвращает rid сразу, не блокируясь. Результат доставляется на callback, если он задан, или доступен через Cloud Storage по rid.
callback
URLопциональный
Webhook-URL для получения результата краула. Обязателен при async=true, если вы не хотите опрашивать вручную.
store
booleanfalse
Сохранять закраулленную страницу в Cloud Storage. Возвращает rid в дополнение к телу.

Формат вывода

Ответ по умолчанию - сырой body страницы, ровно то, что браузер получил бы после рендеринга и разрешения анти-бот защиты. Для большинства пайплайнов это правильная форма (ваш downstream-парсер работает с HTML напрямую). Используйте format=json, когда хотите получить метаданные (статус, финальный URL, RID, заголовки) собранные в один конверт вместо разделения между заголовками ответа и body. Используйте scraper= или autoparse=true, когда целевой сайт - один из тех, для которых у нас уже есть парсер: вы полностью пропускаете этап парсинга и получаете чистые структурированные поля вместо сырой разметки.

format
html | json | mdhtml
Выберите конверт ответа. html возвращает сырую страницу с метаданными в заголовках ответа. json оборачивает страницу плюс все метаданные в один JSON-объект. md конвертирует страницу в GitHub-Flavored Markdown - сочетайте с md_readability=true, чтобы сначала убрать навигацию/сайдбар/рекламу.
md_readability
booleanfalse
Имеет смысл только с format=md. При true Crawlbase прогоняет readability-проход по странице перед конвертацией в Markdown - убирает «обвязку» (навигацию, сайдбар, футер, рекламные блоки) и сохраняет основной контент статьи. Лучше всего подходит для конвертации блог-постов и статей в чистый LLM-контекст.
pretty
booleanfalse
Имеет смысл только при format=json. Форматирует JSON-envelope с отступами и переносами строк для удобства чтения; в продакшене отключайте, чтобы ответы оставались компактными.
scraper
stringопциональный
Применяет встроенный scraper для извлечения структурированных данных вместо возврата HTML. Пример: amazon-product-details.
autoparse
booleanfalse
Автоматически определяет тип страницы и применяет подходящий scraper. Удобно, когда нужно «дай JSON, если можешь».

Управление ответом

Эти параметры меняют то, что содержит ответ, или то, как Crawlbase решает, что запрос был успешен. Используйте get_headers и get_cookies, когда вам нужны заголовки ответа целевого сайта или значения Set-Cookie, переданные обратно вам (они удаляются по умолчанию). Используйте custom_success_codes, когда цель закономерно возвращает не-2xx статус, который ваш пайплайн должен трактовать как чистый fetch - без этого Crawlbase будет повторять такие ответы за вас.

get_headers
booleanfalse
Показывает заголовки ответа целевого сайта. Они возвращаются с префиксом original_header_* в заголовках ответа или сгруппированными под original_headers при format=json.
get_cookies
booleanfalse
Показывает значения Set-Cookie целевого сайта. Они возвращаются как original_set_cookie в заголовках ответа или под тем же ключом при format=json.
custom_success_codes
stringопциональный
Список HTTP-статус-кодов через запятую, которые следует трактовать как успешные - например, custom_success_codes=403,429,503. Crawlbase не будет их повторять, а исходный статус сохранится в original_status. Используйте, когда цель закономерно возвращает эти коды для вашего endpoint (auth-gated API, регион-блокированные страницы, body которых вам всё же нужен).

POST-запросы

Используйте POST, когда целевой endpoint ожидает тело запроса - отправки форм, JSON-API, GraphQL, всё, что не помещается в query string. Тот же endpoint, те же параметры, та же форма ответа, что и у GET; меняются только HTTP-метод и тело.

POST работает только с Normal token

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-телом

Переопределите дефолтный form-urlencoded content type через 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))
}

Примечание: целевой сайт сам решает, принимать ли body. Crawlbase пересылает запрос честно - если цель возвращает 4xx, потому что форма body неверна, это отражается в original_status, а не в pc_status. См. Errors для паттерна ветвления.

PUT-запросы

PUT работает так же, как POST - тот же endpoint, те же параметры, те же правила кодирования body. Единственное различие - 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.

Не используйте POST/PUT для спама

Crawlbase активно мониторит POST- и PUT-трафик. Отправка тел запросов, нацеленных на сторонние сайты, которыми вы не владеете - спам в комментариях, мошеннические отправки форм, скриптовое создание аккаунтов - приводит к блокировке исходного аккаунта при первом обнаружении. Используйте эти методы для легитимных API-интеграций, ваших собственных staging- и production-endpoint-ов и явно разрешённой автоматизации.

Ответ

Успешные ответы возвращают целевую страницу в теле. Метаданные находятся в заголовках ответа.

Заголовки

ЗаголовокОписание
pc_statusСтатус-код Crawlbase. 200 = успех.
original_statusHTTP-статус с целевого сайта.
urlФинальный URL после редиректов.
ridID запроса. Возвращается при async=true или store=true.
content-typeMIME-тип тела (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-Typetext/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, в body, с 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 — один из:

КодЗначение
200OK
201Created
204No Content
301Moved Permanently
302Found - только когда редирект был выполнен и вернул контент
404Not Found
410Gone

Любой другой 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' -G
from 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)
}

Гео-маршрутизированный запрос

# Route through Germany; the echoed JSON confirms the exit country
curl 'https://api.crawlbase.com/?token=YOUR_TOKEN' \
  --data-urlencode 'url=https://ipinfo.io/json' \
  --data-urlencode 'country=DE' -G
from crawlbase import CrawlingAPI

api = CrawlingAPI({'token': 'YOUR_TOKEN'})
res = api.get('https://ipinfo.io/json', {'country': 'DE'})
print(res['body'])
const { CrawlingAPI } = require('crawlbase');
const api = new CrawlingAPI({ token: 'YOUR_TOKEN' });

const res = await api.get('https://ipinfo.io/json', { country: 'DE' });
console.log(res.body);
require 'crawlbase'

api = Crawlbase::API.new(token: 'YOUR_TOKEN')
res = api.get('https://ipinfo.io/json', country: 'DE')
puts res.body
<?php
use Crawlbase\CrawlingAPI;

$api = new CrawlingAPI(['token' => 'YOUR_TOKEN']);
$res = $api->get('https://ipinfo.io/json', ['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://ipinfo.io/json", map[string]string{
        "country": "DE",
    })
    fmt.Println(res.Body)
}

Асинхронный краулинг с webhook

Когда использовать async

Async освобождает ваш слот конкурентности в момент постановки запроса в очередь, поэтому долгий краулинг не съедает бюджет. Используйте его для медленных целей (тяжёлый JS, длинный page_wait), когда нужно прокачать большой объём.

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 ready
from 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 later
const { 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 later
require '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 later
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://example.com", map[string]string{
        "async":    "true",
        "callback": "https://your-app.com/webhook",
    })
    fmt.Println(res.RID) // → returned immediately; result POSTed to callback later
}

Режим прокси

Тот же Crawling API можно вызывать как HTTP/HTTPS-прокси вместо REST-endpoint-а - это полезно, когда у вас уже есть скрейпер, скрипт автоматизации браузера или HTTP-клиент, который уже поддерживает конфигурацию прокси, и вы предпочитаете поставить Crawlbase перед ним, а не переписывать слой запросов.

Направьте клиент на smartproxy.crawlbase.com:8001 (HTTPS, рекомендуется) или smartproxy.crawlbase.com:8000 (HTTP) и передавайте токен как имя пользователя прокси. Все функции Crawling API - JS-рендеринг, обход анти-бот защиты, гео-маршрутизация - применяются идентично; меняется только форма запроса.

Режим прокси vs. Smart AI Proxy

Два продукта используют один и тот же hostname, но разные порты - легко перепутать. Возможности по сути одинаковы у обоих (маршрутизация по странам, эмуляция устройства, сессии, кастомные заголовки, JS-рендеринг через контролы CrawlbaseAPI-*); различаются подписка, по которой вы биллитесь, и уровень параллельности / потоков, который эта подписка обеспечивает:

  • Crawling API в режиме прокси (этот раздел) → порты 8000 / 8001. Маршрутизация через ваш план Crawling API: та же месячная квота, тот же бюджет конкурентности, та же тарификация за успешные запросы, что и в REST-режиме. Выбирайте этот вариант, если уже оплачиваете Crawling API и хотите получить интерфейс в форме прокси наряду с REST-эндпоинтом.
  • Smart AI Proxy (отдельный продукт, см. Smart AI Proxy) → порты 8012 / 8013. Отдельный SKU со своей подпиской и своей моделью потоков / параллельности, рассчитанной на proxy-first пайплайны скрейпинга, которые уже работают с высоким числом потоков. Та же сеть и те же управляющие заголовки внутри - выбор сводится к тому, какой контракт и какая форма параллельности подходят под ваше использование.

Правило большого пальца: выбирайте продукт, подписка на который у вас уже есть (или чья модель ценообразования соответствует форме вашего трафика). Поверхность возможностей одинакова; порты просто маршрутизируют вас в нужную полосу биллинга и конкурентности.

Быстрый старт

Первый вызов из вашего shell - 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 с пустым body, и тогда original_status будет 200, а pc_status - 520. Всегда ветвитесь по pc_status, когда решаете, повторять ли запрос.

Самые распространённые сбои, специфичные для Crawling API:

КодЗначениеДействие
422url отсутствует или не URL-кодированЗакодируйте URL перед отправкой. Большинство клиентов (libcurl --data-urlencode, Python requests, Node fetch) делают это автоматически - но самописные query string часто это пропускают.
520Пустой ответ от целевого сайтаПовторите один раз. Если по-прежнему пусто, переключитесь с Normal на JS token - многие сайты отдают пустой shell не-браузерным user agent-ам и полагаются на JS для наполнения.
521Целевой сайт недоступен / лежитСчитайте это временной upstream-ошибкой. Backoff + повтор; если ситуация сохраняется минутами, сайт действительно лежит.
522Тайм-аут соединения с целевым сайтомПовторите с backoff. Попробуйте другую country, если цель чувствительна к географии.
523Origin недоступен с выбранной точки выходаПовторите без 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-токеном быстрее и используют меньше параллельности. Переходите на JS только когда ответ Normal пуст или заблокирован челленджем.
  • Предпочитайте ajax_wait вместо page_wait. Фиксированные задержки сжигают параллельность на каждом запросе, даже на быстрых. ajax_wait возвращает результат в момент, когда страница достигает network-idle - в среднем обычно быстрее и медленнее только на действительно долго загружающихся страницах.
  • Прокачивайте большие объёмы через async + webhook. Синхронный режим — правильный выбор по умолчанию для разовых и интерактивных сценариев. Для пакетных задач от нескольких сотен URL и больше асинхронный режим (или Enterprise Crawler) сохраняет ваш бюджет конкурентности свободным для новых отправок, пока существующие краулинги завершаются.
  • Переиспользуйте сессии для stateful-сценариев. Если целевой сайт требует авторизованной сессии или cookies корзины, удерживайте session ID и передавайте его в последующих запросах, чтобы переиспользовался тот же выходной IP и тот же набор cookies. См. Authentication для шаблона работы с session-cookie.
  • Следите за заголовком remaining. Отступайте до того, как упрётесь в лимит параллельности, а не узнавайте о нём через 429-е - в ответе передаётся количество оставшихся слотов, так что здоровый клиент засыпает проактивно, а не реагирует на ошибки.