Toda vez que a API da CaptchaAI devolve ERROR_ZERO_BALANCE, o motivo é sempre o mesmo: não sobrou saldo para a próxima tarefa. Antes de revisar credenciais ou reiniciar workers, confira o saldo — é a causa de quase todo chamado de suporte. Este guia mostra como diagnosticar rápido, automatizar o alerta e manter a automação de pé.
Resposta rápida: o que fazer agora
- Confirme o saldo com o endpoint
getbalance(exemplo logo abaixo). - Se estiver zerado, adicione fundos em captchaai.com antes de reenviar tarefas.
- Configure o monitor de saldo para não passar por isso de novo.
Principais causas do saldo zerado
| Causa | Frequência | Resolução |
|---|---|---|
| Saldo da conta esgotado | Mais comum | Adicione fundos em captchaai.com |
| Consumo acima do previsto | Comum | Configure monitoramento de saldo |
| Chave de API exposta acidentalmente | Raro | Gire a chave de API e revise o uso |
| Forma de pagamento expirada | Ocasional | Atualize os dados de cobrança |
Contexto: orçamento em BRL vs. cobrança em USD
Comum em times que orçam em reais e rodam workers em
sa-east-1: como a cobrança é em dólar, um pico de volume no fechamento do mês esgota o saldo mais rápido do que o previsto.
Como checar o saldo pela API
import requests
def check_balance(api_key):
"""Check current CaptchaAI balance."""
resp = requests.get(
"https://ocr.captchaai.com/res.php",
params={"key": api_key, "action": "getbalance", "json": 1},
timeout=10,
)
data = resp.json()
if data.get("status") == 1:
return float(data["request"])
raise RuntimeError(f"Balance check failed: {data.get('request')}")
balance = check_balance("YOUR_API_KEY")
print(f"Balance: ${balance:.4f}")
Como usar isso no seu pipeline
- Rode antes de qualquer lote grande, não só depois que o erro já apareceu.
- Registre o valor retornado em log para acompanhar a tendência de consumo.
- Combine com o
min_balancedo próximo exemplo para parar antes do saldo zerar de vez.
Trate o saldo zerado sem derrubar a automação
Deixar o processo estourar exceção no meio de um lote é o pior cenário. O padrão abaixo cacheia o saldo e só lança erro específico quando a conta chega a zero.
import requests
import time
import logging
logger = logging.getLogger(__name__)
class BalanceAwareSolver:
"""Solver that handles zero balance without crashing."""
def __init__(self, api_key, min_balance=0.50):
self.api_key = api_key
self.min_balance = min_balance
self._last_balance_check = 0
self._cached_balance = None
def solve(self, params):
"""Solve CAPTCHA with balance pre-check."""
# Check balance every 5 minutes
if time.time() - self._last_balance_check > 300:
self._check_balance()
if self._cached_balance is not None and self._cached_balance < 0.01:
raise InsufficientBalanceError(
f"Balance too low: ${self._cached_balance:.4f}. "
"Add funds at https://captchaai.com"
)
try:
return self._submit_and_poll(params)
except ZeroBalanceError:
self._cached_balance = 0.0
logger.error("ERROR_ZERO_BALANCE — add funds at captchaai.com")
raise
def _check_balance(self):
"""Check and cache balance."""
try:
resp = requests.get(
"https://ocr.captchaai.com/res.php",
params={
"key": self.api_key,
"action": "getbalance",
"json": 1,
},
timeout=10,
)
data = resp.json()
if data.get("status") == 1:
self._cached_balance = float(data["request"])
self._last_balance_check = time.time()
if self._cached_balance < self.min_balance:
logger.warning(
f"Low balance: ${self._cached_balance:.4f} "
f"(threshold: ${self.min_balance:.2f})"
)
except Exception as e:
logger.debug(f"Balance check failed: {e}")
def _submit_and_poll(self, params):
"""Submit task and poll for result."""
data = {"key": self.api_key, "json": 1, **params}
resp = requests.post(
"https://ocr.captchaai.com/in.php", data=data, timeout=30,
)
result = resp.json()
if result.get("status") != 1:
error = result.get("request", "")
if error == "ERROR_ZERO_BALANCE":
raise ZeroBalanceError("Account balance is zero")
raise RuntimeError(f"Submit failed: {error}")
task_id = result["request"]
time.sleep(10)
for _ in range(24):
resp = requests.get(
"https://ocr.captchaai.com/res.php",
params={
"key": self.api_key, "action": "get",
"id": task_id, "json": 1,
},
timeout=15,
)
data = resp.json()
if data.get("status") == 1:
return data["request"]
if data["request"] != "CAPCHA_NOT_READY":
raise RuntimeError(data["request"])
time.sleep(5)
raise TimeoutError("Solve timeout")
class ZeroBalanceError(Exception):
"""Raised when account has no balance."""
pass
class InsufficientBalanceError(Exception):
"""Raised when balance is below minimum threshold."""
pass
Pontos de atenção
- O cache de 5 minutos evita checar o saldo a cada chamada — ajuste o intervalo conforme o volume.
InsufficientBalanceErroreZeroBalanceErrorsão exceções separadas: trate cada uma conforme a criticidade do chamador.- Sempre logue o erro antes de propagar — é o rastro que explica uma parada em produção.
Configure alertas antes que o saldo chegue a zero
Um monitor em background evita surpresas: ele consulta o saldo em intervalos e dispara um alerta ao cruzar o limite definido, antes do ERROR_ZERO_BALANCE aparecer em produção.
import smtplib
from email.message import EmailMessage
import threading
import time
import logging
logger = logging.getLogger(__name__)
class BalanceMonitor:
"""Monitor balance and send alerts when low."""
def __init__(self, api_key, alert_threshold=1.00, check_interval=600):
self.api_key = api_key
self.alert_threshold = alert_threshold
self.check_interval = check_interval
self._alert_sent = False
self._running = False
def start(self):
"""Start background monitoring."""
self._running = True
thread = threading.Thread(target=self._monitor_loop, daemon=True)
thread.start()
logger.info("Balance monitor started")
def stop(self):
"""Stop monitoring."""
self._running = False
def _monitor_loop(self):
"""Check balance periodically."""
while self._running:
try:
balance = self._get_balance()
logger.info(f"Balance: ${balance:.4f}")
if balance <= 0:
self._send_alert("CRITICAL: CaptchaAI Zero Balance",
f"Balance is ${balance:.4f}. Solving will fail.")
elif balance < self.alert_threshold and not self._alert_sent:
self._send_alert("WARNING: CaptchaAI Low Balance",
f"Balance: ${balance:.4f} (threshold: ${self.alert_threshold:.2f})")
self._alert_sent = True
elif balance >= self.alert_threshold:
self._alert_sent = False # Reset alert flag
except Exception as e:
logger.error(f"Monitor error: {e}")
time.sleep(self.check_interval)
def _get_balance(self):
"""Check account balance."""
resp = requests.get(
"https://ocr.captchaai.com/res.php",
params={"key": self.api_key, "action": "getbalance", "json": 1},
timeout=10,
)
data = resp.json()
if data.get("status") == 1:
return float(data["request"])
raise RuntimeError(data.get("request"))
def _send_alert(self, subject, body):
"""Send email alert. Replace with your notification method."""
logger.critical(f"{subject}: {body}")
# Implement email, Slack webhook, or other notification here
# Usage
monitor = BalanceMonitor("YOUR_API_KEY", alert_threshold=2.00)
monitor.start()
Onde plugar o alerta
- Troque o
logger.criticalpor um webhook do Slack ou um e-mail transacional. - Ajuste
check_intervalpara não sobrecarregar a API em contas de alto volume. - Rode o monitor como processo separado do worker de resolução, não dentro do mesmo loop.
Calcule o custo estimado antes de um lote grande
Antes de um lote de milhares de tarefas, vale confirmar se o saldo cobre o volume planejado. A função abaixo estima o consumo por tipo de CAPTCHA e compara com o saldo disponível.
# Approximate costs per CAPTCHA type
COST_PER_SOLVE = {
"recaptcha_v2": 0.003,
"recaptcha_v3": 0.004,
"turnstile": 0.002,
"geetest": 0.003,
"image": 0.001,
"bls": 0.002,
}
def estimate_cost(captcha_type, quantity):
"""Estimate cost for a batch of solves."""
rate = COST_PER_SOLVE.get(captcha_type, 0.003)
total = rate * quantity
return total
def check_budget(api_key, captcha_type, planned_solves):
"""Check if balance covers planned solves."""
balance = check_balance(api_key)
estimated = estimate_cost(captcha_type, planned_solves)
if balance >= estimated:
print(f"Budget OK: ${balance:.4f} covers ~{int(balance / COST_PER_SOLVE[captcha_type])} solves")
return True
else:
shortfall = estimated - balance
print(f"Need ${shortfall:.4f} more for {planned_solves} {captcha_type} solves")
return False
# Check before a large batch
check_budget("YOUR_API_KEY", "recaptcha_v2", 5000)
Antes de confiar na estimativa
- Os valores de
COST_PER_SOLVEsão aproximados — confirme o custo real no painel após o lote. - Rode
check_budgetno início do job, não apenas uma vez ao dia. - Em lotes com vários tipos de CAPTCHA, some as estimativas por tipo antes de comparar com o saldo.
Degrade com elegância quando o saldo acaba
Nem toda automação deve parar quando o saldo zera. Às vezes é melhor pular o item, enfileirar para depois do recarregamento, ou interromper tudo — o padrão abaixo deixa a escolha configurável.
class GracefulSolver:
"""Fall back to manual or skip when balance is zero."""
def __init__(self, api_key, on_zero_balance="skip"):
self.api_key = api_key
self.on_zero_balance = on_zero_balance # "skip", "queue", "raise"
self._pending_queue = []
self.solver = BalanceAwareSolver(api_key)
def solve_or_degrade(self, params, item_id=None):
"""Try to solve, degrade gracefully on zero balance."""
try:
return self.solver.solve(params)
except (ZeroBalanceError, InsufficientBalanceError):
return self._handle_zero(params, item_id)
def _handle_zero(self, params, item_id):
"""Handle zero balance based on configured strategy."""
if self.on_zero_balance == "skip":
logger.warning(f"Skipping CAPTCHA for item {item_id} — no balance")
return None
elif self.on_zero_balance == "queue":
self._pending_queue.append({"params": params, "item_id": item_id})
logger.info(f"Queued item {item_id} — {len(self._pending_queue)} pending")
return None
else: # "raise"
raise ZeroBalanceError("No balance — stopping automation")
def retry_pending(self):
"""Retry queued items after balance is refilled."""
if not self._pending_queue:
return []
results = []
remaining = []
for item in self._pending_queue:
try:
token = self.solver.solve(item["params"])
results.append({"item_id": item["item_id"], "token": token})
except (ZeroBalanceError, InsufficientBalanceError):
remaining.append(item)
break # Stop retrying — still no balance
self._pending_queue = remaining + self._pending_queue[len(results) + len(remaining):]
return results
Escolhendo a estratégia certa
skipfunciona bem para itens não críticos, onde perder um item é aceitável.queueé melhor quando o lote precisa ficar completo — só retome depois de recarregar o saldo.raisefaz sentido quando a automação não deve continuar rodando sem saldo, evitando erros em cascata.
Diagnóstico rápido de sintomas
| Sintoma | Causa | Correção |
|---|---|---|
| ERROR_ZERO_BALANCE em toda requisição | Conta sem saldo | Adicione fundos em captchaai.com |
| Saldo cai rápido demais | Chave de API exposta ou código ineficiente | Gire a chave e revise os logs de uso |
| Saldo mostra positivo mas o erro persiste | Atraso de cache/sincronização | Aguarde 1 minuto e tente novamente |
| Não consigo adicionar fundos | Problema na forma de pagamento | Atualize a forma de pagamento no painel |
Se nada disso resolver, siga esta ordem de verificação:
- Rode
check_balancede novo e confirme o valor exato retornado. - Revise os logs de uso das últimas 24 horas em busca de picos anormais.
- Abra um chamado de suporte com o
task_idda última tentativa, se o saldo estiver correto mesmo assim.
Perguntas frequentes
O que acontece com tarefas que já estavam em andamento quando o saldo zera?
Tarefas já enviadas costumam ser concluídas normalmente. O erro só aparece nas próximas submissões, sem saldo para abrir uma nova tarefa.
Como diferencio ERROR_ZERO_BALANCE de uma chave de API inválida?
ERROR_ZERO_BALANCE é conta sem fundos; chave inválida retorna ERROR_WRONG_USER_KEY. Se check_balance autenticar normalmente, o problema é saldo, não credencial.
Faz sentido manter um saldo mínimo de segurança?
Sim, em alto volume principalmente. Um min_balance como no BalanceAwareSolver evita enviar tarefas com saldo insuficiente, poupando chamadas à API.
Depois de pagar, quanto tempo leva até o saldo aparecer disponível?
A maioria dos métodos credita o saldo na hora. Transferências bancárias podem levar de 1 a 2 dias úteis.
A CaptchaAI cobra por tarefas que falham?
Não. A cobrança ocorre só em soluções bem-sucedidas com token ou resposta válida.
Guias relacionados
Mantenha o saldo sempre em dia — adicione fundos na sua conta CaptchaAI.