Files
robotex/docs/API.md
T
protokeyandClaude Opus 5.5 5ed2cfbb3d feature: действие captcha value=image — капча-картинка через 2captcha (#109)
Снимок элемента картинки на текущей странице (без повторной загрузки src) уходит
в 2captcha как обычная капча (method=base64), ответ набирается в поле target
как у typing и возвращается в result действия. Необязательные подсказки:
min_len, max_len, numeric, lang, case_sensitive. Клиент: Action.captcha_image.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 16:17:25 +04:00

18 KiB
Raw Blame History

API

Как встраивать API в свой клиент (UA, прокси, сессии, повторы) — Usage.md.

Задача открывает страницу в Chrome (через прокси клиента, если он задан), при необходимости проходит антибот (Cloudflare Turnstile, reCAPTCHA), выполняет список действий и возвращает cookies, результаты действий и, по запросу, HTML и скриншот. Задачи выполняет сервис worker из очереди; API работает в двух режимах:

  • POST /v1/solve — синхронно: ответ приходит, когда задача выполнена;
  • POST /v2/tasks + GET /v2/tasks/{id} — асинхронно, с возможностью держать окно браузера открытым между задачами (см. API v2).

GET /health (и GET /) — {"status": "ok", "browser_slots": N, "free_slots": M, "workers": [...]}: слоты всех живых воркеров; workers — по каждому worker_id, browser_slots, free_slots, windows (открытые окна), running (выполняемые задачи).

Авторизация

Все эндпоинты, кроме /health, требуют ключ клиента в заголовке X-API-Key. Без ключа, с неверным или отозванным ключом — 401. Ключи выпускает администратор (make key-create name=<клиент>, см. Deployment.md).

Задача и окно сессии видны только клиенту, который их создал: с другим ключом GET /v2/tasks/{id} даёт 404, а чужое открытое окно не используется.

Запрос

POST /v1/solve
Content-Type: application/json
X-API-Key: <key>
Cookie: session_id=<id>        # необязательно, см. «Сессии»

{
	"url": "https://freelancehunt.com/projects",
	"proxy": "http://user:pass@host:port",
	"captcha_type": "cloudflare",
	"actions": [
		{"type": "wait_for", "selector": "a[href*=\"/project/\"]", "timeout": 60}
	],
	"html": true,
	"timeout": 120
}
Поле Тип По умолчанию Описание
url string — обязательно. Страница, которую открываем
proxy string null scheme://[user:pass@]host:port, схема http, https или socks5 (Chrome не поддерживает socks5 с логином/паролем). По exit-IP прокси выставляются locale и timezone браузера; без прокси — настройки браузера по умолчанию
actions array — обязательно, может быть пустым. Действия, см. ниже
captcha_type string null провайдер антибота: cloudflare или recaptcha. Без него шаг антибота пропускается — для сайтов за Cloudflare обязательно
captcha string null ключ сервиса распознавания (2captcha), если антибот не проходится без решения капчи
captcha_detect_locator string null локатор, по которому определяется, что на странице антибот
captcha_success_locator string null локатор, по которому определяется, что антибот пройден
html bool false вернуть HTML страницы после выполнения действий
screenshot bool false вернуть скриншот видимой области (PNG, base64) после действий — в том числе при ошибке действия или антибота
timeout int, с 60 таймаут навигации
cookies array null cookies для установки до навигации, формат Playwright: {name, value, domain, path, expires, httpOnly, secure, sameSite} или {name, value, url}. Заменяют cookies профиля: перед установкой все cookies профиля удаляются ([] — просто очистить). Без поля cookies профиля сохраняются
disable_resources bool false не грузить картинки, шрифты, медиа
blocked_domains array null домены, запросы к которым блокируются
retries int 1 попыток навигации
retry_delay float, с 1.0 пауза между попытками
wait int, мс 0 пауза после загрузки страницы
google_search bool false выставить Referer как переход из поиска Google

Действия

Выполняются по порядку после прохождения антибота. Если антибот не пройден, действия не выполняются (code: challenge_failed). На первой ошибке действия выполнение останавливается.

Обязательные поля проверяются до запуска браузера: неизвестный type, нет selector/value, captcha без ключа в запросе, неверный proxy — ответ 422 с описанием в detail (стандартный формат FastAPI).

type Поля Что делает
wait_for selector, timeout (1–7200 с, по умолчанию 30) ждёт появления элемента; при таймауте — ошибка действия
click selector клик по элементу
submit selector клик и ожидание навигации
fill selector, value мгновенная вставка значения в поле
typing selector, value ввод как у человека: подвод курсора, клик, очистка поля, посимвольный набор с паузами 50–200 мс; медленнее fill, но не выдаёт автоматизацию
inner_text selector возвращает текст элемента (HTML без тегов) в result
mouse_move selector наведение курсора на элемент; нет элемента — ошибка selector not found
delay value (секунды строкой, 0–600) пауза
inner_html selector возвращает HTML элемента в result; нет элемента — ошибка selector not found
captcha selector, value (тип: cloudflare, recaptcha, hcaptcha) решить капчу внутри страницы через 2captcha, нужен captcha в запросе
captcha (value: "image") selector (картинка), target (поле ввода); подсказки необязательны: min_len, max_len (1–20), numeric, lang, case_sensitive капча-картинка «код с картинки»: скриншот элемента selector на текущей странице (без повторной загрузки, код не меняется) уходит в 2captcha, ответ набирается в target как у typing и возвращается в result. Не решена или таймаут 2captcha — ошибка действия. numeric: 1 — только цифры, 2 — только буквы, 3 — цифры или буквы, 4 — и цифры, и буквы
pass_challenge selector (локатор успеха, необязательно) повторно пройти антибот, например после перехода на другую страницу; если не пройден — ошибка действия

Капча-картинка в форме регистрации — решить, затем отправить форму. Если сайт отклонил код и показал новую картинку, повторите задачу v2 в открытом окне сессии (browser_idle_timeout): страница не перезагружается.

{"captcha": "<ключ 2captcha>", "browser_idle_timeout": 5, "actions": [
	{"type": "captcha", "selector": "#popup-signup img.captcha", "value": "image",
	 "target": "#popup-signup input[name=\"captcha\"]", "min_len": 4, "max_len": 6},
	{"type": "submit", "selector": "#popup-signup button[type=submit]"}
]}

Ответ

Формат один для всех случаев: status — "ok" или "err", при ошибке — code и error. Поля без значения не возвращаются.

Успех (антибот пройден, действия выполнены), HTTP 200:

{
	"status": "ok",
	"user_agent": "Mozilla/5.0 (X11; Linux x86_64) ... Chrome/155.0.0.0 Safari/537.36",
	"cookies": [{"name": "cf_clearance", "value": "...", "domain": ".freelancehunt.com", "...": "..."}],
	"actions": [
		{"type": "wait_for", "ok": true, "duration_ms": 14}
	],
	"html": "<head>...</head><body>...</body>",
	"duration_ms": 6405
}
Поле Описание
status "ok" — антибот пройден и все действия выполнены, иначе "err"
code при "err": причина, см. таблицу ниже
error при "err": текст ошибки; для упавшего действия — <type>: <текст>
user_agent родной UA браузера, с которым проходили антибот. Используйте его в своём HTTP-клиенте вместе с cf_clearance (см. Usage.md)
cookies все cookies контекста
actions по элементу на выполненное действие: type, ok, result (результат или текст ошибки), duration_ms
html только при "html": true
screenshot только при "screenshot": true: PNG в base64
duration_ms общее время обработки запроса
code HTTP Когда Что ещё есть в ответе
no_slots 503 + Retry-After только /v1/solve: все слоты браузеров заняты, или воркер не взял задачу за V1_QUEUE_WAIT секунд (задача отменяется) —
request_failed 200 ошибка до действий: навигация, прокси, неизвестный captcha_type actions: []
challenge_failed 200 антибот не пройден за отведённое время, действия не выполнялись cookies, html, screenshot
action_failed 200 упало действие, выполнение остановлено на нём cookies, html, screenshot, actions до упавшего включительно

Примеры:

{"status": "err", "code": "action_failed", "error": "inner_html: selector not found: #missing",
 "actions": [{"type": "inner_html", "ok": false, "result": "selector not found: #missing", "duration_ms": 26}], "...": "..."}
{"status": "err", "code": "challenge_failed", "error": "cloudflare not passed: timeout (stage: checkbox)", "actions": [], "...": "..."}
{"status": "err", "code": "no_slots", "error": "No slots available"}

Сессии

Ответ ставит cookie session_id (UUID). Если передать её в следующем запросе, браузер откроется с тем же профилем (extra/user_data_dir/<session_id>): cookies, localStorage и отпечаток сохраняются, повторно проходить Cloudflare обычно не нужно. Без cookie или с некорректным значением (не UUID) запрос получает новый профиль.

Сессии изолированы друг от друга: у каждой свой профиль Chrome — cookies, localStorage, кэш.

Отпечаток профиля генерируется при первом запросе сессии и дальше не меняется (robotex_session.json в каталоге профиля):

  • размер экрана и devicePixelRatio десктопного Chrome на Linux из реального распределения (browserforge) — нативной эмуляцией Chrome;
  • шум canvas: свой seed у каждого профиля, поэтому хеш canvas у профилей разный, а внутри профиля стабильный. Меняется младший бит у редких непрозрачных пикселей при чтении canvas (toDataURL, toBlob, getImageData); пустой canvas не меняется.

Остальное — WebGL, hardwareConcurrency, шрифты, UA — не подменяется: это родные значения Chrome, за маскировку автоматизации отвечает Patchright. Полная JS-подмена отпечатка ломала прохождение Cloudflare.

User-Agent и размер окна запросом не задаются — это родные значения браузера (почему — Usage.md). Поле user_agent в запросе даёт 422.

Задачи одной сессии выполняются по очереди: профиль Chrome нельзя открыть дважды. Вторая задача ждёт, пока закончится первая.

API v2

Асинхронный режим: задача ставится в очередь, клиент опрашивает её по ссылке. Тело задачи — как у /v1/solve, плюс:

Поле Тип По умолчанию Описание
browser_idle_timeout int, мин 0 держать окно браузера открытым после задачи столько минут простоя, 0–120. 0 — закрыть сразу, как в v1
url string null необязателен, если окно сессии открыто: тогда действия выполняются на текущей странице, без навигации

POST /v2/tasks

POST /v2/tasks
Content-Type: application/json
Cookie: session_id=<id>        # необязательно

{"url": "https://freelancehunt.com/projects", "captcha_type": "cloudflare", "actions": [], "browser_idle_timeout": 10}

Ответ 202 и cookie session_id:

{"task_id": "0b9c…", "status": "queued", "url": "/v2/tasks/0b9c…"}

Без url и без открытого окна сессии — 422.

GET /v2/tasks/{id}

Только с cookie session_id той сессии, в которой задача создана; иначе, а также для несуществующей или истёкшей задачи — 404.

{"task_id": "0b9c…", "status": "done", "result": {"status": "ok", "user_agent": "…", "cookies": […], "actions": […], "duration_ms": 7120}}
status Значение
queued ждёт свободного слота браузера
running выполняется
done готово, ответ в result — в том же формате, что ответ /v1/solve (со своим status ok/err)
failed задачу не удалось выполнить (например, воркер упал дважды), причина в error

Результат хранится RESULT_TTL_HOURS часов (по умолчанию 24), затем 404. Опрашивать раз в 0,5–2 секунды.

Окно браузера между задачами

С browser_idle_timeout > 0 Chrome сессии не закрывается после задачи. Следующая задача с той же cookie session_id выполняется в этом окне: без запуска Chrome и обычно без повторного антибота, а без url — прямо на текущей странице (например, продолжить заполнение формы). Каждая задача в окно заново отсчитывает простой — по своему browser_idle_timeout; задача с 0 закрывает окно после себя.

  • Открытое окно занимает слот браузера, пока не закроется. Не держите окна дольше, чем нужно.
  • Задача с другим proxy, чем у открытого окна, перезапускает Chrome (прокси задаётся при запуске).
  • cookies в задаче заменяют cookies открытого окна, как и при запуске.
  • Если задача в окне упала с request_failed, окно закрывается; следующая задача откроет его заново из профиля.

DELETE /v2/sessions/current

Закрыть окно сессии (по cookie session_id) досрочно и освободить слот. Если в окне идёт задача — сразу после неё. Ответ {"closed": true}, или {"closed": false}, если окна нет.

Производительность

Каждое окно — отдельный Chrome: около 0,6 ГБ памяти и до 3–4 ядер на пике при прохождении Cloudflare. Замеры и рекомендации по лимитам — в Deployment.md.