A simultaneidade não controlada envia solicitações o com menor latência possível. Isso leva a ERROR_TOO_MUCH_REQUESTS, desperdício de equilíbrio de API e custos imprevisíveis. Um token bucket permite definir uma taxa exata – “não mais que 20 envios por segundo” – enquanto ainda permite rajadas curtas quando a capacidade está disponível.
Como funciona um balde de token
[Bucket] capacity=20, refill=10/sec
Time 0: ████████████████████ 20 tokens available
→ 15 requests consume 15 tokens
Time 0: █████ 5 tokens remain
Time 1s: ███████████████ 15 tokens (5 + 10 refilled)
→ 15 requests consume 15 tokens
Time 1s: (empty) 0 tokens
Time 2s: ██████████ 10 tokens (0 + 10 refilled)
→ Request waits if bucket is empty
Propriedades principais:
- Capacidade — tamanho máximo do burst
- Taxa de recarga — solicitações sustentadas por segundo
- As solicitações aguardam quando o intervalo está vazio (sem rejeição, apenas limitação)
Implementação Python
Balde de token thread-safe
import time
import threading
class TokenBucket:
def __init__(self, capacity, refill_rate):
"""
Args:
capacity: Maximum tokens (burst size)
refill_rate: Tokens added per second
"""
self.capacity = capacity
self.refill_rate = refill_rate
self.tokens = capacity
self.last_refill = time.monotonic()
self.lock = threading.Lock()
def acquire(self, timeout=None):
"""Block until a token is available."""
deadline = time.monotonic() + timeout if timeout else float("inf")
while True:
with self.lock:
self._refill()
if self.tokens >= 1:
self.tokens -= 1
return True
# Check timeout
if time.monotonic() >= deadline:
return False
# Wait before retrying (avoid busy loop)
time.sleep(min(1.0 / self.refill_rate, 0.1))
def _refill(self):
now = time.monotonic()
elapsed = now - self.last_refill
new_tokens = elapsed * self.refill_rate
self.tokens = min(self.capacity, self.tokens + new_tokens)
self.last_refill = now
Solucionador CAPTCHA com taxa limitada
import os
import requests
from concurrent.futures import ThreadPoolExecutor, as_completed
API_KEY = os.environ["CAPTCHAAI_API_KEY"]
# Allow 10 submissions/sec with burst of 20
rate_limiter = TokenBucket(capacity=20, refill_rate=10)
def solve_captcha_rate_limited(sitekey, pageurl):
"""Solve with rate limiting on submission."""
# Wait for token before submitting
rate_limiter.acquire()
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"json": 1
})
data = resp.json()
if data.get("status") != 1:
raise RuntimeError(data.get("request"))
captcha_id = data["request"]
# Polling doesn't need rate limiting (separate concern)
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get", "id": captcha_id, "json": 1
}).json()
if result.get("status") == 1:
return result["request"]
if result.get("request") != "CAPCHA_NOT_READY":
raise RuntimeError(result.get("request"))
raise TimeoutError("Solve timeout")
# Run 100 tasks through rate limiter
tasks = [
{"sitekey": "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
"pageurl": f"https://example.com/p/{i}"}
for i in range(100)
]
with ThreadPoolExecutor(max_workers=30) as executor:
futures = {
executor.submit(
solve_captcha_rate_limited, t["sitekey"], t["pageurl"]
): t for t in tasks
}
for future in as_completed(futures):
task = futures[future]
try:
solution = future.result()
print(f"[OK] {task['pageurl']}")
except Exception as e:
print(f"[ERR] {task['pageurl']}: {e}")
Implementação de JavaScript
Balde de token assíncrono
class TokenBucket {
constructor(capacity, refillRate) {
this.capacity = capacity;
this.refillRate = refillRate; // tokens per second
this.tokens = capacity;
this.lastRefill = Date.now();
this.waitQueue = [];
}
_refill() {
const now = Date.now();
const elapsed = (now - this.lastRefill) / 1000;
this.tokens = Math.min(this.capacity, this.tokens + elapsed * this.refillRate);
this.lastRefill = now;
}
async acquire() {
this._refill();
if (this.tokens >= 1) {
this.tokens -= 1;
return;
}
// Wait until a token is available
const waitTime = ((1 - this.tokens) / this.refillRate) * 1000;
await new Promise((resolve) => setTimeout(resolve, waitTime));
this._refill();
this.tokens -= 1;
}
}
Solucionador de lote com taxa limitada
const axios = require("axios");
const API_KEY = process.env.CAPTCHAAI_API_KEY;
const rateLimiter = new TokenBucket(20, 10); // 20 burst, 10/sec sustained
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function solveCaptchaLimited(sitekey, pageurl) {
// Wait for rate limit token
await rateLimiter.acquire();
const submitResp = await axios.post(
"https://ocr.captchaai.com/in.php",
null,
{
params: {
key: API_KEY,
method: "userrecaptcha",
googlekey: sitekey,
pageurl: pageurl,
json: 1,
},
}
);
if (submitResp.data.status !== 1) {
throw new Error(submitResp.data.request);
}
const captchaId = submitResp.data.request;
for (let i = 0; i < 60; i++) {
await sleep(5000);
const result = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: API_KEY, action: "get", id: captchaId, json: 1 },
});
if (result.data.status === 1) return result.data.request;
if (result.data.request !== "CAPCHA_NOT_READY") {
throw new Error(result.data.request);
}
}
throw new Error("TIMEOUT");
}
// Solve 100 tasks — rate limiter ensures max 10 submissions/sec
async function batchSolve(tasks) {
const results = await Promise.allSettled(
tasks.map((t) => solveCaptchaLimited(t.sitekey, t.pageurl))
);
const solved = results.filter((r) => r.status === "fulfilled").length;
const failed = results.filter((r) => r.status === "rejected").length;
console.log(`Solved: ${solved}, Failed: ${failed}`);
}
Escolhendo Parâmetros
| Carga de trabalho | Capacidade (explosão) | Taxa de recarga (sustentada) |
|---|---|---|
| Raspagem leve | 5 | 2/sec |
| Automação padrão | 20 | 10/sec |
| Pipeline de alto volume | 50 | 30/sec |
| Taxa de transferência máxima | 100 | 50/sec |
Regras básicas:
- Defina a capacidade para taxa de recarga de 2× (permite rajadas de 2 segundos)
- Comece de forma conservadora, aumente enquanto monitora as taxas de erro
- Apenas envios com limite de taxa – a pesquisa é leve e autolimitada
Balde de token versus outros algoritmos
| Algoritmo | Comportamento | mais adequado para |
|---|---|---|
| Balde de tokens | Taxa suave com permissão de burst | Chamadas de API CAPTCHA |
| Balde com vazamento | Taxa de saída fixa, sem rajadas | Requisitos rigorosos de taxas |
| Janela fixa | Contagem por janela de tempo, rajadas de borda | Contadores simples |
| Janela deslizante | Contagem durante o período contínuo | Aplicação precisa de taxas |
O balde de token é o mais adequado padrão – ele permite rajadas naturais (o raspador encontra 20 CAPTCHAs de uma vez) enquanto impõe uma taxa sustentada.
Solução de problemas
| Problema | Causa | Correção |
|---|---|---|
| Solicitações ainda estão sendo limitadas | Limitador de taxa definido acima do permitido pela API | Taxa de recarga mais baixa para corresponder aos limites do CaptchaAI |
| Alta latência nas solicitações | Tokens esgotados, aguardando recarga | Aumente a capacidade para cenários de explosão |
| Memória crescendo | Fila de espera se acumulando | Defina um tamanho máximo de fila; rejeitar solicitações em excesso |
| Limitador de taxa não compartilhado entre processos | Apenas na memória | Use bucket de token baseado em Redis para limitação de taxa distribuída |
Perguntas frequentes
Devo limitar a taxa de envios, pesquisas ou ambos?
Somente envios com limite de taxa. As solicitações de pesquisa são leves e autoaceleradas via time.sleep(5). Limitar excessivamente os aumentos de pesquisa resolvem a latência sem benefícios.
Como faço para lidar com ERROR_TOO_MUCH_REQUESTS apesar da limitação de taxa?
Seu limite de taxa está muito alto. Reduza a taxa de recarga. Verifique também se vários processos compartilham a mesma chave de API – taxa agregada em todos os processos.
Posso usar um limitador de taxa por tipo de CAPTCHA?
Sim – crie buckets de token separados para diferentes tipos de CAPTCHA. Isso evita que tarefas reCAPTCHA v2 de alto volume esfomeem os envios do Turnstile.
Artigos relacionados
Próximas etapas
Crie uma solução CAPTCHA com taxa controlada -obtenha sua chave API CaptchaAIe implementar taxas de solicitação sustentáveis.
Guias relacionados: