Turnstile вернул токен, а форма всё равно отвечает ошибкой? В большинстве случаев дело не в самом решении капчи, а в двух вещах: какие параметры вы отправили в API и куда потом подставили токен. Решатель отдаёт валидный токен — а дальше страница его отклоняет из-за неточного pageurl, чужого sitekey или неправильного поля.
Почти каждый сбой Cloudflare Turnstile попадает на один из трёх этапов, и диагностику стоит начинать именно с того, чтобы определить этот этап:
- Этап отправки — задачу не приняли (
in.phpвернул код ошибки). - Этап опроса — задача принята, но опрос
res.phpне доходит до готового токена. - Этап проверки на странице — токен получен, но целевая страница его не принимает.
CaptchaAI решает Cloudflare Turnstile с высокой долей успешных решений, как правило, менее чем за 10 секунд. Поэтому если интеграция «падает», причину почти всегда стоит искать в своих параметрах, а не в решателе.
Три ошибки, которые дают больше всего провалов именно с Turnstile:
- Неточный
pageurl— особенно на страницах проверки Cloudflare, где контекст строже. - Не тот sitekey — снят с чужого элемента или другого экземпляра виджета.
- Токен подставлен не туда — страница ждёт
cf-turnstile-response, обратный вызов или и то и другое.
Шпаргалка: ошибка → причина → исправление
Начните диагностику с этой таблицы: найдите свой симптом, определите этап и переходите к нужному разделу ниже.
| Ошибка / симптом | Этап | Вероятная причина | Что делать |
|---|---|---|---|
ERROR_WRONG_USER_KEY |
Отправка | Неверный API-ключ | Проверьте 32-значный ключ |
ERROR_KEY_DOES_NOT_EXIST |
Отправка | Ключ не привязан к аккаунту | Проверьте панель управления |
ERROR_ZERO_BALANCE |
Отправка | Нет свободных потоков | Подождите или смените тариф |
ERROR_PAGEURL |
Отправка | Отсутствует pageurl |
Добавьте полный URL |
ERROR_BAD_PARAMETERS |
Отправка | Нет sitekey, method или pageurl | Проверьте все обязательные поля |
CAPCHA_NOT_READY |
Опрос | Решение ещё идёт | Подождите 5 секунд, повторите |
ERROR_WRONG_ID_FORMAT |
Опрос | Нечисловой ID капчи | Используйте точный ID из in.php |
ERROR_WRONG_CAPTCHA_ID |
Опрос | Неверный ID капчи | Сверьте ID из ответа на отправку |
ERROR_EMPTY_ACTION |
Опрос | Нет action=get |
Добавьте параметр action |
| Токен отклонён страницей | Проверка | Не то поле, не сработал обратный вызов, не тот URL | Проверьте имя поля, вызовите обратный вызов, сверьте точный URL |
| Второе решение не проходит | Проверка | Повторное использование токена | Запрашивайте новый токен на каждую отправку |
Turnstile или Cloudflare Challenge: что перед вами
Сначала убедитесь, с чем вы вообще имеете дело. Часть «отклонённых токенов» на самом деле объясняется тем, что перед вами не встроенный виджет Turnstile, а полноэкранная проверка Cloudflare Challenge — а это другой сценарий интеграции.
| Сигнал | Cloudflare Turnstile | Cloudflare Challenge |
|---|---|---|
| Что вы видите | Встроенный виджет на странице (флажок или невидимый) | Полноэкранный экран проверки Cloudflare |
| Что возвращает CaptchaAI | Токен для подстановки в форму | Cookie, подтверждающий прохождение проверки |
| Метод API | turnstile |
cloudflare_challenge |
| Нужен ли прокси | Необязателен | Обязателен |
Если перед вами полноэкранная проверка Cloudflare, а не встроенный виджет, используйте решатель Cloudflare Challenge: он возвращает cookie прохождения проверки и требует прокси. Если же это обычный виджет — читайте дальше.
Ошибки на этапе отправки задачи
Эти ошибки возникают при отправке задачи на https://ocr.captchaai.com/in.php — то есть до самого решения дело ещё не дошло, задачу просто не приняли в очередь.
| Код ошибки | Причина | Что делать |
|---|---|---|
ERROR_WRONG_USER_KEY |
Неверный формат API-ключа: он должен быть длиной 32 символа. | Сверьте ключ на странице captchaai.com/api.php и скопируйте его целиком, без пробелов и переносов. |
ERROR_KEY_DOES_NOT_EXIST |
Ключ отформатирован правильно, но не привязан к активной учётной записи. | Откройте панель управления, убедитесь, что аккаунт активен, а ключ актуален. |
ERROR_ZERO_BALANCE |
Нет свободных потоков в вашем тарифе — все заняты другими задачами. | Дождитесь освобождения потоков, снизьте параллелизм или перейдите на тариф с большим числом потоков — например, с BASIC ($15/мес, 5 потоков) на STANDARD ($30/мес, 15 потоков). |
ERROR_PAGEURL |
Отсутствует параметр pageurl. |
Добавьте полный URL — протокол, домен и путь (пример ниже). |
ERROR_BAD_PARAMETERS |
Обязательные параметры отсутствуют или переданы в неверном формате. | Проверьте набор обязательных полей (таблица ниже). |
| Ответ HTML или коды 500/502 | Временный сбой на стороне сервера. | Подождите 5–10 секунд и повторите запрос. |
Для ERROR_PAGEURL передавайте URL целиком — протокол, домен и путь:
pageurl=https://https://staging.example.com/qa-login
Для ERROR_BAD_PARAMETERS проверьте, что для Turnstile переданы все обязательные параметры:
| Параметр | Тип | Обязателен | Описание |
|---|---|---|---|
key |
Строка | Да | Ваш API-ключ CaptchaAI |
method |
Строка | Да | Должно быть turnstile |
sitekey |
Строка | Да | sitekey виджета Turnstile |
pageurl |
Строка | Да | Полный URL страницы |
Необязательные, но полезные параметры:
| Параметр | Тип | Описание |
|---|---|---|
action |
Строка | Значение data-action или параметра action из turnstile.render() |
proxy |
Строка | Формат: login:password@IP:PORT |
proxytype |
Строка | HTTP, HTTPS, SOCKS4, SOCKS5 |
Где искать sitekey виджета Turnstile
sitekey — параметр, который чаще всего оказывается неправильным. Вот три места, где его можно взять.
Вариант 1 — атрибут data-sitekey:
<div class="cf-turnstile" data-sitekey="0x4AAAAAAAB1example"></div>
Вариант 2 — вызов turnstile.render():
turnstile.render('#captcha-container', {
sitekey: '0x4AAAAAAAB1example',
callback: function(token) {
document.getElementById('cf-turnstile-response').value = token;
}
});
Вариант 3 — перехват вызова рендеринга (для продвинутых):
Если sitekey подгружается динамически, переопределите turnstile.render до инициализации виджета и перехватите его параметры:
// Inject this before the Turnstile script loads
const originalRender = window.turnstile.render;
window.turnstile.render = function(container, params) {
console.log('Sitekey:', params.sitekey);
console.log('Action:', params.action);
return originalRender.call(this, container, params);
};
Ошибки на этапе опроса результата
Эти ошибки возникают при опросе https://ocr.captchaai.com/res.php — задача уже принята, но до готового токена опрос не доходит.
| Ответ | Что это значит | Что делать |
|---|---|---|
CAPCHA_NOT_READY |
Это не ошибка: решение ещё идёт. Turnstile на CaptchaAI обычно решается менее чем за 10 секунд. | Подождите 5 секунд и повторите опрос. |
ERROR_WRONG_ID_FORMAT |
Идентификатор капчи содержит нечисловые символы. | Используйте точный ID, который вернул in.php, без изменений. |
ERROR_WRONG_CAPTCHA_ID |
Идентификатор не соответствует ни одной отправленной задаче. | Сверьте ID с ответом на отправку задачи. |
ERROR_EMPTY_ACTION |
В запросе на опрос нет параметра action. |
Всегда указывайте action=get (пример ниже). |
ERROR_CAPTCHA_UNSOLVABLE |
Решить не удалось — вероятно, неверный sitekey или неподдерживаемая конфигурация страницы. | Проверьте sitekey, обновите запрос и повторите попытку. |
ERROR_INTERNAL_SERVER_ERROR |
Сбой на стороне сервера. | Подождите 10 секунд и повторите запрос. |
Правильный запрос на опрос с обязательным action=get выглядит так:
https://ocr.captchaai.com/res.php?key=YOUR_KEY&action=get&id=CAPTCHA_ID&json=1
Примечание. Для Turnstile всегда опрашивайте результат с
json=1. В JSON-ответе может прийтиuser_agentрешателя — некоторым страницам под защитой Cloudflare он нужен, чтобы токен успешно прошёл проверку.
Чем Turnstile отличается от других капч
Дальше начинаются самые неочевидные сбои — когда токен получен, но страница его не принимает. Прежде чем разбирать их, держите в голове три особенности Turnstile: именно из-за них большинство таких ошибок и возникает.
1. Точный URL страницы важнее, чем обычно
Токены Turnstile жёстко привязаны к контексту страницы. На страницах проверки Cloudflare (полноэкранный экран верификации) даже чуть-чуть другой путь в pageurl приводит к тому, что токен отклоняется. Это не «мелочь» — это самая частая причина, по которой валидный токен не проходит.
2. Два пути подстановки токена
Полученный токен подставляют одним из двух способов, и неправильный вариант просто не сработает:
| Способ | Когда применять |
|---|---|
Скрытое поле — записать в cf-turnstile-response (иногда и в g-recaptcha-response) |
Когда на странице обычная форма со скрытым полем |
Функция обратного вызова — вызвать функцию из turnstile.render() или data-callback |
Когда страница проверяет токен программно, без формы |
3. Токен одноразовый
Токен Turnstile проверяется только один раз. Если автоматизация случайно отправит его дважды или возникнет состояние гонки, вторая попытка завершится ошибкой.
Когда токен получен, но страница его отклоняет
Такие сбои отлаживать сложнее всего: API успешно вернул токен, а целевая страница всё равно его не принимает.
Сбой 1: токен записан не в то поле
Симптом: форма отправляется, но страница выдаёт ошибку проверки или перезагружается.
Страницы Turnstile ждут токен в разных полях:
cf-turnstile-response— основное скрытое поле Turnstile;g-recaptcha-response— некоторые страницы используют его как запасной вариант.
Исправление: проверьте форму на наличие обоих полей. В автоматизации браузера удобно записать токен сразу в оба:
# Selenium — inject into both fields for safety
driver.execute_script("""
var cfField = document.querySelector('[name="cf-turnstile-response"]');
var gField = document.querySelector('[name="g-recaptcha-response"]');
if (cfField) cfField.value = arguments[0];
if (gField) gField.value = arguments[0];
""", token)
Сбой 2: не сработал обратный вызов
Симптом: токен в поле есть, но форма всё равно не отправляется.
Причина: страница использует функцию обратного вызова вместо скрытого поля (или вдобавок к нему). Обратный вызов выполняет дополнительную логику — например, разблокирует кнопку отправки или шлёт AJAX-запрос.
Исправление: найдите и вызовите обратный вызов вручную:
// Check data-callback attribute
const callbackName = document.querySelector('.cf-turnstile').getAttribute('data-callback');
if (callbackName && window[callbackName]) {
window[callbackName](token);
}
// Or if it was passed in turnstile.render()
// You may need to intercept the render call to capture it
Сбой 3: не тот контекст страницы
Симптом: токен отклонён, хотя sitekey верный и решение свежее.
Причина: pageurl в запросе к API не совпадает с фактическим контекстом страницы. Особенно часто это встречается на:
- страницах проверки Cloudflare — в URL могут быть важные параметры запроса или части пути;
- одностраничных приложениях (SPA) — видимый URL может отличаться от того, по которому загрузился виджет Turnstile.
Исправление: во вкладке «Сеть» DevTools найдите точный URL, с которого подгружается виджет Turnstile, и передавайте именно его в pageurl.
Сбой 4: повторное использование токена
Симптом: первое решение проходит, последующие — нет.
Причина: токены Turnstile одноразовые. После проверки на стороне Cloudflare токен становится недействительным.
Исправление: запрашивайте новое решение под каждую отправку формы. Не кэшируйте и не переиспользуйте токены.
Python: полный цикл решения Turnstile
import time
import requests
API_KEY = "YOUR_CAPTCHAAI_API_KEY"
SITEKEY = "0x4AAAAAAAB1example"
PAGE_URL = "https://https://staging.example.com/qa-login"
SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"
def solve_turnstile(api_key, sitekey, pageurl):
"""Submit a Turnstile challenge and return the solved token."""
# Submit
submit_resp = requests.post(
SUBMIT_URL,
data={
"key": api_key,
"method": "turnstile",
"sitekey": sitekey,
"pageurl": pageurl,
"json": 1,
},
timeout=30,
)
submit_resp.raise_for_status()
submit_data = submit_resp.json()
if submit_data.get("status") != 1:
raise RuntimeError(f"Submit failed: {submit_data}")
captcha_id = submit_data["request"]
print(f"Task created — captcha ID: {captcha_id}")
# Wait before first poll (Turnstile is fast — 10 seconds is usually enough)
time.sleep(10)
# Poll for result
for _ in range(60):
result_resp = requests.get(
RESULT_URL,
params={
"key": api_key,
"action": "get",
"id": captcha_id,
"json": 1,
},
timeout=30,
)
result_resp.raise_for_status()
result_data = result_resp.json()
if result_data.get("request") == "CAPCHA_NOT_READY":
time.sleep(5)
continue
if result_data.get("status") == 1:
return result_data["request"]
raise RuntimeError(f"Polling error: {result_data}")
raise TimeoutError("Turnstile solve timed out")
# Usage
token = solve_turnstile(API_KEY, SITEKEY, PAGE_URL)
print(f"Solved token: {token[:80]}...")
# Inject into cf-turnstile-response and/or g-recaptcha-response
# Then submit the form
Node.js: полный цикл решения Turnstile
const API_KEY = "YOUR_CAPTCHAAI_API_KEY";
const SITEKEY = "0x4AAAAAAAB1example";
const PAGE_URL = "https://https://staging.example.com/qa-login";
const SUBMIT_URL = "https://ocr.captchaai.com/in.php";
const RESULT_URL = "https://ocr.captchaai.com/res.php";
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function solveTurnstile(apiKey, sitekey, pageurl) {
// Submit
const submitResp = await fetch(SUBMIT_URL, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
key: apiKey,
method: "turnstile",
sitekey: sitekey,
pageurl: pageurl,
json: "1",
}),
});
const submitData = await submitResp.json();
if (submitData.status !== 1) {
throw new Error(`Submit failed: ${JSON.stringify(submitData)}`);
}
const captchaId = submitData.request;
console.log(`Task created — captcha ID: ${captchaId}`);
// Turnstile is fast — wait 10 seconds before first poll
await sleep(10_000);
// Poll for result
for (let i = 0; i < 60; i++) {
const resultResp = await fetch(
`${RESULT_URL}?${new URLSearchParams({
key: apiKey,
action: "get",
id: captchaId,
json: "1",
})}`
);
const resultData = await resultResp.json();
if (resultData.request === "CAPCHA_NOT_READY") {
await sleep(5_000);
continue;
}
if (resultData.status === 1) {
return resultData.request;
}
throw new Error(`Polling error: ${JSON.stringify(resultData)}`);
}
throw new Error("Turnstile solve timed out");
}
// Usage
solveTurnstile(API_KEY, SITEKEY, PAGE_URL)
.then((token) => {
console.log(`Solved token: ${token.slice(0, 80)}...`);
// Inject into cf-turnstile-response and/or g-recaptcha-response
})
.catch(console.error);
Пример: парсер под нагрузкой из европейского региона
Практический сценарий из тех, что встречаются у RU-команд. Парсер развёрнут в европейском облачном регионе или на площадке в Центральной Азии, идёт пакетный сбор данных, и на пике Turnstile встречается на каждой сессии. Здесь всплывают сразу две проблемы:
- Нехватка потоков. Если задачи уходят быстрее, чем решаются, часть из них упирается в
ERROR_ZERO_BALANCE. Считайте число потоков от пиковой параллельной нагрузки, а не от среднего. - Нестабильная сеть. На мобильных и трансграничных каналах запросы к
res.phpиногда обрываются, поэтому опрос должен переживать таймауты: повторяйте попытку с экспоненциальной задержкой, а не одним циклом без запаса.
И отдельная оговорка про данные: собирайте только те данные, которые вы вправе обрабатывать (для RF-читателей ориентир — 152-ФЗ «О персональных данных»). Это вопрос вашей осмотрительности, а не функция CaptchaAI.
Часто задаваемые вопросы
За сколько CaptchaAI решает Turnstile?
Как правило, менее чем за 10 секунд. Пока решение идёт, res.php возвращает CAPCHA_NOT_READY — это не ошибка, а сигнал повторить опрос через 5 секунд.
Сколько потоков нужно, чтобы решать Turnstile параллельно?
Один поток — это одна задача в работе. Если отправлять капчи быстрее, чем они решаются, часть запросов упрётся в ERROR_ZERO_BALANCE. Считайте потоки от пиковой нагрузки: BASIC ($15/мес, 5 потоков) держит до пяти одновременных решений, STANDARD ($30/мес, 15 потоков) — до пятнадцати.
Почему первый токен проходит, а второй — нет?
Токены Turnstile одноразовые: после проверки на стороне Cloudflare токен становится недействительным. Запрашивайте новое решение под каждую отправку формы и не кэшируйте токены.
Обязателен ли параметр action при опросе результата?
Да. В запросе к res.php всегда указывайте action=get, иначе вернётся ERROR_EMPTY_ACTION. Параметр action из turnstile.render() — это другое: его передают при отправке задачи, если страница его использует.
Нужен ли прокси для Turnstile?
Для отдельного виджета Turnstile прокси необязателен — добавляйте proxy и proxytype, только если этого требует ваш сценарий. А вот для полноэкранной проверки Cloudflare Challenge прокси обязателен.
Как починить интеграцию Turnstile
Если интеграция Turnstile не работает, пройдитесь по чек-листу:
- Проверьте sitekey — возьмите его из
data-sitekeyилиturnstile.render(). - Проверьте pageurl — используйте точный URL, включая протокол и путь.
- Проверьте путь токена — страница ждёт
cf-turnstile-response,g-recaptcha-responseили обратный вызов? - Опрашивайте с
json=1— для результатов Turnstile всегда используйте JSON-ответ. - Не переиспользуйте токены — запрашивайте новое решение на каждую отправку.
Начните с решателя Cloudflare Turnstile от CaptchaAI, сверьте параметры с документацией по API, а если нужен разбор механики виджета — прочитайте статью как устроен Cloudflare Turnstile.