Каждый лишний запрос к res.php — это задержка и нагрузка на клиента. URL обратного вызова (pingback) убирает опрос: CaptchaAI сам присылает решённый токен на ваш сервер, как только капча решена, и на очереди из сотен задач в минуту это резко снижает число API-запросов и задержку доставки.
Этот подход особенно заметен в конвейерах, где решение CAPTCHA — лишь один шаг в более длинной цепочке (проверка формы, оформление заказа, парсинг результатов страницы). Пока клиент опрашивает res.php, поток фактически простаивает; при обратном вызове тот же поток освобождается для другой работы и получает уведомление, когда токен готов.
Как это работает
Разница между двумя подходами хорошо видна на схеме:
Standard polling approach:
Submit ──▶ Wait ──▶ Poll ──▶ Poll ──▶ Poll ──▶ Result
(many requests, wasted time between polls)
Callback approach:
Submit (with callback URL) ──▶ ... CaptchaAI solves ...
│
Your server receives POST ◀───────────────┘
(one request, instant delivery)
Когда обратный вызов выгоднее опроса
Обратный вызов не всегда нужен — для разового скрипта опрос проще и его не нужно защищать. Ориентируйтесь на объём и инфраструктуру:
| Сценарий | Что выбрать |
|---|---|
| Разовый скрипт, единичные решения | Опрос |
| Продакшен-пайплайн с высоким объёмом задач | Обратный вызов |
| Serverless (AWS Lambda, Cloud Functions) | Опрос — нет постоянного сервера |
| Сервис за NAT/firewall, без публичного IP | Опрос |
| Микросервисная архитектура | Обратный вызов |
Например, команда, которая парсит данные с нескольких сайтов на серверах в европейском облаке, обычно берёт обратный вызов: десятки задач приходят на один эндпоинт вместо опроса res.php из каждого воркера. Для локальной отладки эндпоинта до деплоя удобен туннель через ngrok.
Решение не обязано быть окончательным на весь проект — многие команды начинают с опроса на этапе прототипа, а после переезда в продакшен переключаются на обратный вызов, когда нагрузка и число одновременных задач вырастают настолько, что опрос начинает заметно нагружать сеть и клиентские потоки.
Отправка запроса с параметром pingback
Добавьте pingback в тело запроса на отправку задачи — остальной запрос не меняется:
import requests
API_KEY = "YOUR_API_KEY"
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": "SITE_KEY",
"pageurl": "https://example.com",
"pingback": "https://your-server.com/captcha-callback",
"json": 1,
})
result = resp.json()
task_id = result["request"]
print(f"Task submitted: {task_id}")
# No polling needed — result comes via webhook
Работает для всех поддерживаемых типов CAPTCHA
Параметр pingback одинаково подключается к любому методу — меняется только method и связанные с типом параметры:
# reCAPTCHA v2
data = {
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": "SITE_KEY",
"pageurl": "https://example.com",
"pingback": "https://your-server.com/callback",
"json": 1,
}
# Turnstile
data = {
"key": API_KEY,
"method": "turnstile",
"sitekey": "SITE_KEY",
"pageurl": "https://example.com",
"pingback": "https://your-server.com/callback",
"json": 1,
}
# Image CAPTCHA
data = {
"key": API_KEY,
"method": "base64",
"body": base64_image,
"pingback": "https://your-server.com/callback",
"json": 1,
}
Приёмник обратных вызовов на Flask
Минимальный сервер, который принимает POST от CaptchaAI и будит поток, ожидающий результат:
from flask import Flask, request
import threading
import logging
app = Flask(__name__)
logger = logging.getLogger(__name__)
# Store results by task ID
results = {}
events = {}
@app.route("/captcha-callback", methods=["POST", "GET"])
def captcha_callback():
"""Receive solved CAPTCHA tokens from CaptchaAI."""
# CaptchaAI sends parameters as query string or form data
task_id = request.args.get("id") or request.form.get("id")
code = request.args.get("code") or request.form.get("code")
if not task_id or not code:
logger.warning("Callback missing id or code")
return "ERROR", 400
logger.info("Received result for task %s", task_id)
results[task_id] = code
# Notify waiting threads
event = events.get(task_id)
if event:
event.set()
return "OK"
def wait_for_result(task_id, timeout=120):
"""Wait for a callback result."""
event = threading.Event()
events[task_id] = event
if task_id in results:
return results.pop(task_id)
event.wait(timeout=timeout)
if task_id in results:
return results.pop(task_id)
raise TimeoutError(f"No callback received for task {task_id}")
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
Полный цикл: отправка и ожидание через обратный вызов
Собираем отправку и приёмник в одну функцию, которую можно вызывать из остального кода как обычный solve():
import requests
import threading
import time
API_KEY = "YOUR_API_KEY"
CALLBACK_URL = "https://your-server.com/captcha-callback"
def solve_with_callback(sitekey, pageurl):
"""Submit CAPTCHA and wait for callback."""
# Submit with callback URL
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"pingback": CALLBACK_URL,
"json": 1,
})
result = resp.json()
if result.get("status") != 1:
raise RuntimeError(f"Submit failed: {result.get('request')}")
task_id = result["request"]
print(f"Task {task_id} submitted, waiting for callback...")
# Wait for callback on the receiver
token = wait_for_result(task_id, timeout=120)
print(f"Token received via callback: {token[:50]}...")
return token
Защита эндпоинта обратного вызова
Публичный URL, который принимает POST, — это открытая дверь. Прежде чем доверять телу запроса, проверьте, что задача действительно ваша:
from flask import Flask, request, abort
app = Flask(__name__)
# Store submitted task IDs to validate callbacks
pending_tasks = set()
def submit_captcha(sitekey, pageurl):
"""Submit and track task ID."""
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"pingback": CALLBACK_URL,
"json": 1,
})
task_id = resp.json()["request"]
pending_tasks.add(task_id)
return task_id
@app.route("/captcha-callback", methods=["POST", "GET"])
def secure_callback():
"""Validate callback before processing."""
task_id = request.args.get("id") or request.form.get("id")
# Reject unknown task IDs
if task_id not in pending_tasks:
abort(403)
code = request.args.get("code") or request.form.get("code")
pending_tasks.discard(task_id)
results[task_id] = code
return "OK"
pending_tasks — минимум. В проде добавьте TTL для старых ID и логирование отклонений 403.
Дополнительно стоит логировать сам факт получения callback отдельно от логики его обработки: если эндпоинт временно возвращает 403 из-за гонки (задача ещё не успела попасть в pending_tasks), в логе это будет видно как всплеск отклонений, а не как потеря токенов. На проде такие эндпоинты обычно ставят за обратным прокси с собственным TTL и rate limiting, чтобы один медленный обработчик не блокировал приём следующих callback.
Поиск и устранение неполадок
| Проблема | Причина | Решение |
|---|---|---|
| Callback не приходит | Сервер недоступен из интернета | ngrok для тестов, либо деплой в облако |
| CaptchaAI не принимает URL | Адрес недоступен на момент отправки | Проверьте, что URL публично отвечает |
| Часть результатов теряется | Отдельные callback не доходят | Логируйте входящие, добавьте опрос /res.php |
| Результат приходит дважды | Повтор после сетевого сбоя | Дедуплицируйте по ID задачи |
Обратный вызов и опрос: коротко
| Фактор | Опрос | Обратный вызов |
|---|---|---|
| API-запросы | Много (отправка + N опросов) | 2 (отправка + один callback) |
| Задержка | Зависит от интервала опроса | Доставка сразу после решения |
| Нагрузка | Клиент опрашивает непрерывно | CaptchaAI сам присылает результат |
| Сложность | Проще — нужен только клиент | Нужен публичный эндпоинт и его защита |
| Требования к сети | Только исходящие соединения | Должен принимать входящий POST |
| Оптимально для | Простых скриптов | Продакшен-пайплайнов с высоким объёмом |
Часто задаваемые вопросы
Сколько раз CaptchaAI повторяет обратный вызов, если сервер недоступен?
Число попыток не гарантировано. Рассчитывайте, что часть callback может не дойти — например, из-за короткого простоя сервера при деплое или сетевого сбоя, — и держите резервный опрос /res.php для задач без результата за ожидаемое время. Это дешевле, чем пытаться угадать точную политику повторов извне.
Как отличить настоящий обратный вызов CaptchaAI от постороннего запроса на тот же URL?
Сверяйте id из запроса со списком задач, которые вы сами отправляли (см. раздел про защиту эндпоинта). Всё, чего нет в списке, — отклоняйте с кодом 403 и не выполняйте побочных действий (запись в БД, уведомления) до этой проверки.
Нужен ли callback URL с HTTPS?
Для продакшена — да. HTTP годится только для тестов: токен и ID задачи идут в открытом виде, и их может перехватить кто угодно на пути пакета.
Можно ли совмещать обратный вызов и опрос в одном сервисе?
Да, это обычная практика: callback — основной канал, опрос /res.php — резервный на случай, если он не пришёл за 60–120 секунд. Такая связка закрывает и редкие потери callback, и временную недоступность вашего эндпоинта во время деплоя.
Связанные руководства
Меньше запросов, быстрее токены — настройте обратные вызовы CaptchaAI и уберите цикл опроса из своего пайплайна.