Troubleshooting

ERROR_ZERO_BALANCE: solução de problemas de pagamento e cobrança

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

  1. Confirme o saldo com o endpoint getbalance (exemplo logo abaixo).
  2. Se estiver zerado, adicione fundos em captchaai.com antes de reenviar tarefas.
  3. 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_balance do 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.
  • InsufficientBalanceError e ZeroBalanceError sã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.critical por um webhook do Slack ou um e-mail transacional.
  • Ajuste check_interval para 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_SOLVE são aproximados — confirme o custo real no painel após o lote.
  • Rode check_budget no 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

  • skip funciona 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.
  • raise faz 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:

  1. Rode check_balance de novo e confirme o valor exato retornado.
  2. Revise os logs de uso das últimas 24 horas em busca de picos anormais.
  3. Abra um chamado de suporte com o task_id da ú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.

Os comentários estão desativados para este artigo.