10 000 CAPTCHA в час — это не «более быстрый API», а ~42 запроса, которые находятся в работе одновременно, постоянно. Отдельное решение reCAPTCHA v2 не станет быстрее — вы просто перекрываете достаточно запросов, чтобы за один и тот же интервал решения завершалось сразу несколько десятков задач. Дальше — расчёт нагрузки, схема конвейера и рабочий код для CaptchaAI на Python и Node.js.
Сколько потоков нужно на самом деле
Возьмём медианное время решения reCAPTCHA v2 в качестве примера — 15 секунд (официальный SLA-потолок для этого типа у CaptchaAI — менее 60 секунд, но в спокойных условиях медиана обычно ниже):
- Последовательно, одна задача за другой: 3600 с / 15 с = 240 решений/час.
- Чтобы дойти до 10 000/час, нужно держать в полёте ~42 одновременных запроса без пауз.
Отсюда и вся архитектура ниже: не «ускорить» вызов к API, а научиться отправлять и опрашивать десятки задач параллельно, не блокируя друг друга.
Та же формула для других целей
Формула масштабируется и на меньшие цели — полезно прикинуть заранее, прежде чем настраивать MAX_CONCURRENT:
| Цель | Формула | Нужная конкурентность |
|---|---|---|
| 1000 задач/час | 1000 × 15 / 3600 | ~4 запроса одновременно |
| 5000 задач/час | 5000 × 15 / 3600 | ~21 запрос одновременно |
| 10 000 задач/час | 10000 × 15 / 3600 | ~42 запроса одновременно |
| 20 000 задач/час | 20000 × 15 / 3600 | ~83 запроса одновременно |
Чем выше медианное время решения конкретного типа CAPTCHA, тем больше запросов нужно держать в полёте для той же цели по часам — формула одна и та же, меняется только время решения.
Как выбрать параметры нагрузки
| Параметр | Консервативный | Сбалансированный | Агрессивный |
|---|---|---|---|
| MAX_CONCURRENT | 20 | 50 | 100 |
| INITIAL_WAIT | 15 секунд | 12 секунд | 10 с |
| POLL_INTERVAL | 7 с | 5 с | 3 с |
| MAX_POLL_ATTEMPTS | 30 | 25 | 20 |
| Ожидаемая пропускная способность | ~4800/час | ~10 000/час | ~18 000/час |
Начинайте с консервативного профиля и постепенно поднимайте MAX_CONCURRENT, пока не увидите уменьшение отдачи на каждый добавленный запрос или рост доли ошибок.
Полезно сразу прикинуть, какой тариф CaptchaAI покрывает нужную конкурентность. 42 одновременных запроса из расчёта выше комфортно укладываются в ADVANCE ($90/мес, 50 потоков); для агрессивного профиля на 100 concurrent-запросов понадобится PREMIUM ($170/мес, 100 потоков). Тарификация идёт по потокам, а не по числу решённых задач, — переход на более крупный план не меняет логику кода, только MAX_CONCURRENT.
Архитектура конвейера
┌──────────┐ ┌────────────┐ ┌─────────────┐ ┌──────────┐
│ Task │────▶│ Submit │────▶│ CaptchaAI │────▶│ Result │
│ Queue │ │ Workers │ │ API │ │ Store │
│ (Redis) │ │ (async) │ │ │ │ (DB) │
└──────────┘ └────────────┘ └─────────────┘ └──────────┘
│ ▲
│ ┌──────────┐ │
└───▶│ Poll │────┘
│ Workers │
└──────────┘
Схема разбирается на четыре узла: очередь задач хранит ожидающие CAPTCHA с ключами сайтов и URL-адресами; воркеры отправки параллельно шлют их в CaptchaAI API; воркеры опроса проверяют результат через оптимизированные интервалы, не блокируя отправку новых задач; хранилище результатов сохраняет токены по мере поступления, чтобы остальная система забирала их асинхронно.
Python: асинхронный конвейер
# high_throughput_solver.py
import os
import asyncio
import time
import aiohttp
API_KEY = os.environ.get("CAPTCHAAI_KEY", "YOUR_API_KEY")
BASE_URL = "https://ocr.captchaai.com"
MAX_CONCURRENT = 50 # Max simultaneous solves
POLL_INTERVAL = 5 # Seconds between polls
INITIAL_WAIT = 12 # Seconds before first poll
semaphore = asyncio.Semaphore(MAX_CONCURRENT)
stats = {"submitted": 0, "solved": 0, "failed": 0, "start": 0}
async def solve_one(session, sitekey, pageurl, task_num):
"""Submit and poll a single CAPTCHA."""
async with semaphore:
try:
# Submit
async with session.get(f"{BASE_URL}/in.php", params={
"key": API_KEY, "method": "userrecaptcha",
"googlekey": sitekey, "pageurl": pageurl, "json": "1",
}) as resp:
result = await resp.json(content_type=None)
if result.get("status") != 1:
stats["failed"] += 1
return None
stats["submitted"] += 1
task_id = result["request"]
# Wait before first poll
await asyncio.sleep(INITIAL_WAIT)
# Poll
for _ in range(25):
async with session.get(f"{BASE_URL}/res.php", params={
"key": API_KEY, "action": "get",
"id": task_id, "json": "1",
}) as resp:
poll_result = await resp.json(content_type=None)
if poll_result.get("status") == 1:
stats["solved"] += 1
return poll_result["request"]
if poll_result.get("request") != "CAPCHA_NOT_READY":
stats["failed"] += 1
return None
await asyncio.sleep(POLL_INTERVAL)
stats["failed"] += 1
return None
except Exception as e:
stats["failed"] += 1
return None
async def run_batch(tasks):
"""Process a batch of CAPTCHA tasks concurrently."""
connector = aiohttp.TCPConnector(
limit=MAX_CONCURRENT,
keepalive_timeout=60,
)
async with aiohttp.ClientSession(connector=connector) as session:
coros = [
solve_one(session, task["sitekey"], task["pageurl"], i)
for i, task in enumerate(tasks)
]
results = await asyncio.gather(*coros)
return results
async def main():
# Generate test tasks (replace with your task source)
tasks = [
{
"sitekey": "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
"pageurl": "https://www.google.com/recaptcha/api2/demo",
}
for _ in range(100) # Start with 100 tasks
]
stats["start"] = time.time()
print(f"Processing {len(tasks)} tasks with {MAX_CONCURRENT} concurrent workers")
results = await run_batch(tasks)
elapsed = time.time() - stats["start"]
print(f"\nCompleted in {elapsed:.0f}s")
print(f"Submitted: {stats['submitted']}")
print(f"Solved: {stats['solved']}")
print(f"Failed: {stats['failed']}")
print(f"Throughput: {stats['solved'] / (elapsed / 3600):.0f} solves/hour")
asyncio.run(main())
Node.js: параллельный конвейер на axios
// high_throughput_solver.js
const axios = require('axios');
const https = require('https');
const API_KEY = process.env.CAPTCHAAI_KEY || 'YOUR_API_KEY';
const BASE = 'https://ocr.captchaai.com';
const MAX_CONCURRENT = 50;
const agent = new https.Agent({ keepAlive: true, maxSockets: MAX_CONCURRENT });
const api = axios.create({ baseURL: BASE, httpsAgent: agent, timeout: 30000 });
const stats = { submitted: 0, solved: 0, failed: 0 };
async function solveOne(sitekey, pageurl) {
try {
const submit = await api.get('/in.php', {
params: { key: API_KEY, method: 'userrecaptcha', googlekey: sitekey, pageurl, json: '1' },
});
if (submit.data.status !== 1) { stats.failed++; return null; }
stats.submitted++;
await new Promise(r => setTimeout(r, 12000));
for (let i = 0; i < 25; i++) {
const poll = await api.get('/res.php', {
params: { key: API_KEY, action: 'get', id: submit.data.request, json: '1' },
});
if (poll.data.status === 1) { stats.solved++; return poll.data.request; }
if (poll.data.request !== 'CAPCHA_NOT_READY') { stats.failed++; return null; }
await new Promise(r => setTimeout(r, 5000));
}
stats.failed++;
return null;
} catch { stats.failed++; return null; }
}
async function runWithConcurrency(tasks, limit) {
const results = [];
const executing = new Set();
for (const task of tasks) {
const p = solveOne(task.sitekey, task.pageurl).then(r => {
executing.delete(p);
return r;
});
executing.add(p);
results.push(p);
if (executing.size >= limit) {
await Promise.race(executing);
}
}
return Promise.all(results);
}
(async () => {
const tasks = Array.from({ length: 100 }, () => ({
sitekey: '6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-',
pageurl: 'https://www.google.com/recaptcha/api2/demo',
}));
const start = Date.now();
console.log(`Processing ${tasks.length} tasks, ${MAX_CONCURRENT} concurrent`);
await runWithConcurrency(tasks, MAX_CONCURRENT);
const elapsed = (Date.now() - start) / 1000;
console.log(`\nDone in ${elapsed.toFixed(0)}s`);
console.log(`Solved: ${stats.solved}, Failed: ${stats.failed}`);
console.log(`Throughput: ${(stats.solved / (elapsed / 3600)).toFixed(0)} solves/hour`);
agent.destroy();
})();
Мониторинг пропускной способности
Что стоит смотреть в реальном времени:
| Метрика | На что обращать внимание |
|---|---|
| Решений в минуту | Для цели 10 000/час должно держаться на уровне ~167 |
| Доля ошибок | Не должна превышать 5 %; резкий рост — сигнал снизить параллелизм |
| Глубина очереди | Растёт — добавляйте воркеров; постоянно пуста — ресурсов больше, чем нужно |
| P90 времени решения | Рост означает, что CaptchaAI начинает ограничивать скорость приёма запросов при текущей нагрузке |
Типичные проблемы и решения
| Проблема | Причина | Что делать |
|---|---|---|
| Пропускная способность упирается в ~5000/час | Недостаточная конкурентность | Поднять MAX_CONCURRENT до 80–100 |
| Доля ошибок выше 10 % | Перегрузка API или нерабочие прокси | Снизить параллелизм, проверить состояние прокси |
| Растёт потребление памяти | Задачи накапливаются без ограничения | Обрабатывать результаты по мере поступления, не буферизовать |
ERROR_NO_SLOT_AVAILABLE |
Очередь CaptchaAI занята | Сделать паузу и повторить запрос через 5 секунд |
Частые вопросы
Сколько потоков CaptchaAI нужно для 10 000 задач в час?
Из расчёта выше — около 42 одновременных запросов при среднем времени решения 15 секунд. С запасом на пиковую нагрузку и колебания времени решения разумно ориентироваться на план с 50–100 потоками (ADVANCE или PREMIUM), а не рассчитывать конкурентность впритык.
Можно ли распределить решение CAPTCHA на несколько серверов?
Да. Общая очередь задач (Redis, RabbitMQ) позволяет запускать воркер-скрипт сразу на нескольких машинах — каждый воркер забирает задачи независимо, а лимит MAX_CONCURRENT считается уже на уровне каждого узла отдельно.
Что делать при росте частоты ERROR_NO_SLOT_AVAILABLE?
Это сигнал, что очередь CaptchaAI на вашем аккаунте временно занята при текущей конкурентности. Не наращивайте MAX_CONCURRENT дальше — сначала добавьте экспоненциальную задержку между повторными попытками и посмотрите, стабилизируется ли доля ошибок.
Как быстро расходуется баланс при 10 000 решений в час?
Тарифы CaptchaAI построены на количестве одновременных потоков, а не на числе решённых задач, — при таком темпе важнее следить не за балансом средств, а за занятостью потоков через res.php?action=getbalance, чтобы вовремя понять, что пора переходить на план с бóльшим числом потоков.