API Tutorials

Как решить проблему reCAPTCHA Invisible с помощью API

Форма отправляется без единого клика по чекбоксу, а в HTML нет g-recaptcha-response — это и есть invisible reCAPTCHA. Проверка выполняется в фоне и завершается вызовом JS-колбэка, а не заполнением скрытого поля. Для CaptchaAI разница минимальна: к обычному запросу userrecaptcha достаточно добавить параметр invisible=1. А вот на стороне сайта разница критична — если не найти и не вызвать нужный колбэк, готовый токен просто некуда девать, и форма зависает.

Ошибка, с которой сталкивается большинство команд, — не в получении токена (это стандартный вызов in.php/res.php), а в его внедрении: разработчики по привычке ищут скрытое поле g-recaptcha-response, хотя в подавляющем большинстве invisible-реализаций сайт ждёт window['имяКолбэка'](token). Ниже — рабочий процесс целиком: как распознать invisible reCAPTCHA, отправить задачу в CaptchaAI и корректно вернуть токен на страницу, включая запасной вариант на случай, если явного колбэка не видно.

Не уверены, перед вами стандартная v2 или invisible-версия? Как отличить reCAPTCHA v2 от invisible — признаки, по которым это видно за секунды.

Коротко весь сценарий выглядит так:

  • находите на странице признак invisible-виджета;
  • добавляете invisible=1 к обычному запросу userrecaptcha;
  • получаете токен через in.php/res.php, как для стандартной v2;
  • отдаёте токен не в скрытое поле, а в JS-колбэк сайта.

Что понадобится для решения

  • API-ключ CaptchaAI — получить можно на captchaai.com/api.php
  • Sitekey — значение атрибута data-sitekey на виджете или кнопке
  • URL страницы — полный адрес страницы, где запускается invisible-проверка
  • Инструмент автоматизации — Selenium, Puppeteer или Playwright, чтобы выполнить колбэк в браузере

Шаг 1. Определите invisible reCAPTCHA на странице

Прежде чем звать API, убедитесь, что перед вами действительно invisible-версия, а не обычная v2 с автосабмитом. В разметке страницы встречается один из трёх вариантов:

<!-- Option 1: div with data-size="invisible" -->
<div class="g-recaptcha" data-sitekey="6LdKlZEU..." data-size="invisible" data-callback="onSubmit"></div>

<!-- Option 2: button with data-sitekey (invisible by default) -->
<button data-sitekey="6LdKlZEU..." data-callback="onSubmit">Submit</button>

<!-- Option 3: programmatic execution -->
<script>
  grecaptcha.execute('6LdKlZEU...', {action: 'submit'});
</script>

Если нашли data-size="invisible", кнопку с data-sitekey без обёртки-виджета рядом или вызов grecaptcha.execute() без видимого контейнера — перед вами invisible reCAPTCHA, и дальше в запрос понадобится параметр invisible=1.

Шаг 2. Отправьте задачу в CaptchaAI

Запрос почти не отличается от обычной reCAPTCHA v2 — меняется один параметр. invisible=1 сообщает CaptchaAI, что решение адресовано именно этой разметке; без него совместимость с частью invisible-виджетов не гарантирована.

import requests

response = requests.get("https://ocr.captchaai.com/in.php", params={
    "key": "YOUR_API_KEY",
    "method": "userrecaptcha",
    "googlekey": "6LdKlZEUAAAAAPoxm...",
    "pageurl": "https://example.com/signup",
    "invisible": 1,
    "json": 1
})

task_id = response.json()["request"]
const params = new URLSearchParams({
  key: "YOUR_API_KEY", method: "userrecaptcha",
  googlekey: "6LdKlZEUAAAAAPoxm...",
  pageurl: "https://example.com/signup",
  invisible: 1, json: 1,
});
const res = await fetch(`https://ocr.captchaai.com/in.php?${params}`);
const { request: taskId } = await res.json();

Если виджет ещё и Enterprise-версии, добавьте к тем же параметрам enterprise=1 — остальной процесс не меняется.

Шаг 3. Опросите res.php до готовности токена

Задача решается асинхронно, поэтому сразу в ответе токена нет — только id. Опрашивайте res.php с паузой в несколько секунд, пока status не станет 1.

import time

for _ in range(40):
    time.sleep(5)
    result = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": "YOUR_API_KEY", "action": "get", "id": task_id, "json": 1
    }).json()
    if result.get("status") == 1:
        token = result["request"]
        break
    if result.get("request") != "CAPCHA_NOT_READY":
        raise RuntimeError(f"Error: {result['request']}")

Шаг 4. Передайте токен через колбэк, а не через поле

Это ключевой шаг всего сценария — большинство ошибок интеграции invisible reCAPTCHA случается именно здесь, а не на этапе решения. Сайт с invisible-виджетом обычно ждёт вызов JS-функции: имя колбэка указано в атрибуте data-callback на том же элементе, где стоит data-sitekey. Найдите это имя через DOM и вызовите функцию с полученным токеном в качестве аргумента.

# Selenium example
from selenium import webdriver

driver = webdriver.Chrome()
driver.get("https://example.com/signup")

# Find the callback name
callback = driver.execute_script("""
    var el = document.querySelector('[data-callback]');
    if (el) return el.getAttribute('data-callback');
    var btn = document.querySelector('[data-sitekey]');
    if (btn) return btn.getAttribute('data-callback');
    return null;
""")

# Execute the callback with the token
if callback:
    driver.execute_script(f"window['{callback}']('{token}');")
else:
    # Fallback: fill hidden field and submit
    driver.execute_script(f"""
        document.getElementById('g-recaptcha-response').innerHTML = '{token}';
        document.querySelector('form').submit();
    """)
// Puppeteer example
await page.evaluate((token) => {
  const el = document.querySelector('[data-callback]') || document.querySelector('[data-sitekey]');
  const callbackName = el?.getAttribute('data-callback');

  if (callbackName && window[callbackName]) {
    window[callbackName](token);
  } else {
    document.getElementById('g-recaptcha-response').innerHTML = token;
    document.querySelector('form').submit();
  }
}, token);

Если явного data-callback нет, а форма всё равно не отправляется сама, сработает запасной вариант из примера выше: заполнить скрытое поле g-recaptcha-response и вызвать submit() формы вручную — так устроена часть старых invisible-реализаций, унаследованных от обычной v2.


Пример: прогон из удалённого региона

Команда в Алматы или Минске нередко гоняет автотесты формы регистрации против стенда, который физически стоит в Европе. Это меняет цифры, на которые стоит ориентироваться при интеграции invisible reCAPTCHA:

  • RTT в 150–250 мс на каждый запрос к res.php — обычное дело для межрегионального прогона, а не признак проблемы на стороне CaptchaAI;
  • при нестабильном мобильном или Wi-Fi-соединении тестового окружения задержка может скакать ещё сильнее — закладывайте запас, а не сокращайте цикл опроса;
  • если форма собирает персональные данные пользователя (email, телефон, паспортные поля), тестируйте только на staging-данных, которые вы вправе обрабатывать — это касается и российских проектов под 152-ФЗ, и команд, ориентирующихся на GDPR.

Практический вывод: 40 попыток опроса с паузой 5 секунд из примера выше — стартовая точка, а не жёсткое правило. Для стендов в другом регионе разумно увеличить либо число попыток, либо паузу между ними.


Полная рабочая функция

Тот же цикл «отправить → опросить → вернуть токен», собранный в одну функцию, которую можно переиспользовать в пайплайне без копирования кода по шагам:

import requests
import time

def solve_invisible_recaptcha(api_key, sitekey, page_url):
    submit = requests.get("https://ocr.captchaai.com/in.php", params={
        "key": api_key, "method": "userrecaptcha", "googlekey": sitekey,
        "pageurl": page_url, "invisible": 1, "json": 1
    }).json()

    if submit.get("status") != 1:
        raise RuntimeError(f"Submit error: {submit.get('request')}")

    task_id = submit["request"]

    for _ in range(40):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": api_key, "action": "get", "id": task_id, "json": 1
        }).json()
        if result.get("status") == 1:
            return result["request"]
        if result.get("request") != "CAPCHA_NOT_READY":
            raise RuntimeError(f"Solve error: {result.get('request')}")

    raise TimeoutError("Timed out")

token = solve_invisible_recaptcha("YOUR_API_KEY", "6LdKlZEU...", "https://example.com/signup")

Полный рабочий пример

Фрагментов кода из шагов 1–4 достаточно для интеграции, но в готовом репозитории есть то, что обычно дописывают отдельно:

  • настройка окружения и переменных (.env, зависимости);
  • повторные попытки при сетевых сбоях, а не только при CAPCHA_NOT_READY;
  • обработка ошибок API и логирование.

Смотрите полный пример на GitHub →


Частые вопросы

Вопросы ниже — то, что обычно всплывает уже после первого успешного решения, на этапе продакшен-нагрузки и разбора граничных случаев.

Сколько потоков тарифа CaptchaAI заложить под invisible reCAPTCHA на нагруженной форме?

Отдельного тарифа под invisible-версию нет — тарификация такая же, как у обычной v2, по числу одновременных потоков, а не по количеству отправок формы за день. Ориентир по параллельности запросов, а не по общему трафику за день:

  • при нескольких десятках одновременных заявок в пике (invisible reCAPTCHA обычно укладывается в SLA до 30 секунд) уровня BASIC ($15/мес, 5 потоков) обычно мало;
  • STANDARD ($30/мес, 15 потоков) закрывает такую нагрузку с запасом на пиковые всплески.

Как отличить invisible reCAPTCHA от обычной v2 на странице?

Два быстрых признака:

  • на виджете стоит data-size="invisible", а не видимый чекбокс «Я не робот»;
  • в коде страницы есть прямой вызов grecaptcha.execute() без контейнера рядом.

Если оба признака совпадают — это invisible-версия, и дальше применяется сценарий из этого руководства.

Что будет, если отправить задачу без invisible=1?

Возможны два сценария:

  • CaptchaAI обработает задачу как обычную reCAPTCHA v2, и в части случаев токен всё равно примется;
  • для реализаций с колбэком сайт нередко отклоняет такой токен как невалидный.

Если на шаге 1 вы определили invisible-версию, параметр стоит передавать всегда, а не рассчитывать на первый сценарий.

Обязательно ли использовать headless-браузер, или колбэк можно вызвать без него?

Обязательно нужен движок с поддержкой DOM и JS — invisible reCAPTCHA ожидает вызов функции в контексте страницы, а не HTTP-запрос. Чистый requests/fetch-клиент без Selenium, Puppeteer или Playwright не сможет ни найти data-callback, ни выполнить его.

Можно ли использовать один колбэк для нескольких invisible-виджетов на одной странице?

Технически да, но у каждого виджета должно быть собственное имя функции в data-callback:

  • общий колбэк на несколько виджетов — последний вызов перезапишет токен предыдущего;
  • уникальное имя под каждый sitekey — единственный надёжный вариант для форм с несколькими invisible-полями.

Похожие материалы

Прежде чем переходить к следующей интеграции, пригодятся ещё несколько руководств:


Начните решать invisible reCAPTCHA

Получите API-ключ на captchaai.com/api.php, добавьте invisible=1 к уже работающему запросу reCAPTCHA v2 и подключите шаблон внедрения токена через колбэк из шага 4 — обычно это единственное место, которое приходится дорабатывать при переходе с обычной v2 на invisible.

Комментарии для этой статьи отключены.