DevOps & Scaling

Monitoramento CaptchaAI com Datadog: Métricas e Alertas

O saldo da API zera de madrugada, a taxa de erro do reCAPTCHA dispara depois de um deploy e ninguém percebe até o primeiro ticket de suporte. É esse o problema que a observabilidade resolve: em vez de descobrir a falha pela reclamação do cliente, você descobre pelo alerta — minutos antes. Este guia mostra quais métricas do pipeline de CAPTCHA da CaptchaAI vale a pena rastrear no Datadog, como enviá-las via DogStatsD em Python e Node.js, como montar o painel e quais alertas configurar primeiro.

Métricas essenciais para monitorar o pipeline CaptchaAI

Sete métricas cobrem praticamente todo incidente relevante: volume, sucesso, erro, latência, fila, saldo e saúde dos workers. Comece por elas antes de instrumentar qualquer coisa mais específica.

Métrica Tipo Por que é importante
captcha.solve.count Contador Total de tarefas enviadas — sua linha de base de volume
captcha.solve.success Contador Soluções bem-sucedidas — numerador da taxa de sucesso
captcha.solve.error Contador Falhas por tipo de erro — onde cavar primeiro num incidente
captcha.solve.latency Histograma Tempo entre envio e solução — base dos alertas de p95/p99
captcha.queue.depth Medidor Tarefas pendentes na fila — sinal antecipado de gargalo
captcha.balance Medidor Saldo restante na conta CaptchaAI
captcha.worker.active Medidor Processos worker ativos no host

Enviando métricas do CaptchaAI ao Datadog em Python (DogStatsD)

O caminho mais direto é um decorator que envolve a função de resolução e publica contador de envio, contador de sucesso/erro e histograma de latência a cada chamada, sem espalhar código de métrica pelo pipeline inteiro.

import os
import time
import functools
import requests
from datadog import initialize, statsd

# Initialize Datadog
initialize(
    statsd_host=os.environ.get("DD_AGENT_HOST", "localhost"),
    statsd_port=int(os.environ.get("DD_DOGSTATSD_PORT", "8125"))
)

API_KEY = os.environ["CAPTCHAAI_API_KEY"]
session = requests.Session()


def track_captcha_metrics(captcha_type="recaptcha_v2"):
    """Decorator to track solve metrics."""
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            tags = [f"captcha_type:{captcha_type}"]
            statsd.increment("captcha.solve.count", tags=tags)

            start = time.time()
            try:
                result = func(*args, **kwargs)
                elapsed = time.time() - start

                if "solution" in result:
                    statsd.increment("captcha.solve.success", tags=tags)
                    statsd.histogram("captcha.solve.latency", elapsed, tags=tags)
                else:
                    error = result.get("error", "unknown")
                    statsd.increment(
                        "captcha.solve.error",
                        tags=tags + [f"error:{error}"]
                    )
                return result
            except Exception as e:
                statsd.increment(
                    "captcha.solve.error",
                    tags=tags + [f"error:{type(e).__name__}"]
                )
                raise
        return wrapper
    return decorator


@track_captcha_metrics(captcha_type="recaptcha_v2")
def solve_recaptcha(sitekey, pageurl):
    resp = session.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:
        return {"error": data.get("request")}

    captcha_id = data["request"]
    for _ in range(60):
        time.sleep(5)
        result = session.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 {"solution": result["request"]}
        if result.get("request") != "CAPCHA_NOT_READY":
            return {"error": result.get("request")}
    return {"error": "TIMEOUT"}


def report_balance():
    """Send balance as a gauge metric."""
    resp = session.get("https://ocr.captchaai.com/res.php", params={
        "key": API_KEY, "action": "getbalance", "json": 1
    })
    data = resp.json()
    if data.get("status") == 1:
        balance = float(data["request"])
        statsd.gauge("captcha.balance", balance)
        return balance
    return None


def report_queue_depth(depth):
    """Report current queue depth."""
    statsd.gauge("captcha.queue.depth", depth)


def report_worker_count(active, total):
    """Report worker health."""
    statsd.gauge("captcha.worker.active", active)
    statsd.gauge("captcha.worker.total", total)

Repare que track_captcha_metrics é um decorator reaproveitável: qualquer função de resolução — reCAPTCHA, Turnstile, GeeTest v3 — ganha as mesmas três métricas só por receber a anotação, com captcha_type na tag para segmentar depois no Datadog.

Enviando métricas do CaptchaAI ao Datadog em Node.js

Em Node.js, a lib hot-shots cobre o mesmo papel do DogStatsD Python, com globalTags fixando o ambiente (env:production, env:staging) em toda métrica emitida pelo processo.

const { StatsD } = require("hot-shots");
const axios = require("axios");

const API_KEY = process.env.CAPTCHAAI_API_KEY;

const dogstatsd = new StatsD({
  host: process.env.DD_AGENT_HOST || "localhost",
  port: parseInt(process.env.DD_DOGSTATSD_PORT || "8125", 10),
  prefix: "captcha.",
  globalTags: [`env:${process.env.NODE_ENV || "development"}`],
});

async function solveCaptchaWithMetrics(sitekey, pageurl, captchaType = "recaptcha_v2") {
  const tags = [`captcha_type:${captchaType}`];
  dogstatsd.increment("solve.count", 1, tags);
  const startTime = Date.now();

  try {
    const result = await solveCaptcha(sitekey, pageurl);
    const elapsed = (Date.now() - startTime) / 1000;

    if (result.solution) {
      dogstatsd.increment("solve.success", 1, tags);
      dogstatsd.histogram("solve.latency", elapsed, tags);
    } else {
      dogstatsd.increment("solve.error", 1, [...tags, `error:${result.error}`]);
    }

    return result;
  } catch (err) {
    dogstatsd.increment("solve.error", 1, [...tags, `error:${err.message}`]);
    throw err;
  }
}

async function solveCaptcha(sitekey, pageurl) {
  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) {
    return { error: submitResp.data.request };
  }

  const captchaId = submitResp.data.request;
  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    const pollResp = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: API_KEY, action: "get", id: captchaId, json: 1 },
    });
    if (pollResp.data.status === 1) return { solution: pollResp.data.request };
    if (pollResp.data.request !== "CAPCHA_NOT_READY") {
      return { error: pollResp.data.request };
    }
  }
  return { error: "TIMEOUT" };
}

async function reportBalance() {
  try {
    const resp = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: API_KEY, action: "getbalance", json: 1 },
    });
    if (resp.data.status === 1) {
      const balance = parseFloat(resp.data.request);
      dogstatsd.gauge("balance", balance);
      return balance;
    }
  } catch (err) {
    console.error("Balance check failed:", err.message);
  }
  return null;
}

// Report balance every minute
setInterval(reportBalance, 60000);

module.exports = { solveCaptchaWithMetrics, reportBalance };

Se os workers rodam numa região próxima do Brasil (por exemplo, sa-east-1 na AWS, São Paulo), lembre-se de que o RTT até o endpoint da CaptchaAI se soma ao tempo total de resolução — vale medir a latência de rede isolada antes de calibrar o limite de alerta de p95 abaixo, para não confundir latência de rede com lentidão do solver.

Painel do Datadog para o pipeline CaptchaAI

Este JSON já vem pronto com quatro widgets — taxa de sucesso vs. erro, percentis de latência, saldo atual e profundidade da fila. Importe direto no Datadog para ter o painel de pé em minutos, sem montar cada gráfico manualmente.

{
  "title": "CaptchaAI Pipeline",
  "widgets": [
    {
      "definition": {
        "type": "timeseries",
        "title": "Solve Rate (Success vs Error)",
        "requests": [
          {"q": "sum:captcha.solve.success{*}.as_count()"},
          {"q": "sum:captcha.solve.error{*}.as_count()"}
        ]
      }
    },
    {
      "definition": {
        "type": "timeseries",
        "title": "Solve Latency (p50, p95, p99)",
        "requests": [
          {"q": "avg:captcha.solve.latency{*}"},
          {"q": "percentile:captcha.solve.latency{*},0.95"},
          {"q": "percentile:captcha.solve.latency{*},0.99"}
        ]
      }
    },
    {
      "definition": {
        "type": "query_value",
        "title": "API Balance",
        "requests": [{"q": "avg:captcha.balance{*}"}]
      }
    },
    {
      "definition": {
        "type": "timeseries",
        "title": "Queue Depth",
        "requests": [{"q": "avg:captcha.queue.depth{*}"}]
      }
    }
  ]
}

Ao criar tags customizadas em cima destas métricas, evite incluir dados pessoais do usuário final (e-mail, CPF, IP completo) nos valores das tags — mantém o diagnóstico útil e a coleta alinhada à LGPD sem perder granularidade.

Alertas do Datadog para saldo, erro e fila da CaptchaAI

Seis alertas cobrem os cenários que realmente derrubam um pipeline em produção: saldo zerando, taxa de erro subindo, latência disparando, fila entupindo e worker morto.

Alerta Condição Gravidade
Saldo baixo captcha.balance < 10 Aviso
Saldo crítico captcha.balance < 2 Crítico
Alta taxa de erro Taxa de erro > 10% em 5 minutos Aviso
Pico de latência Latência p95 > 120 s em 10 minutos Aviso
Fila entupindo Profundidade da fila > 100, crescendo por 5 min Aviso
Worker inativo captcha.worker.active == 0 Crítico
# Datadog monitor definition (API create)
- type: metric alert
  name: "CaptchaAI Low Balance"
  query: "avg(last_5m):avg:captcha.balance{*} < 10"
  message: "CaptchaAI balance is low: {{value}}. Top up to avoid solve failures."
  tags:

    - team:scraping
    - service:captcha

Comece pelos dois alertas de saldo — são os mais baratos de configurar e os que mais evitam interrupção total do pipeline por falta de crédito.

Problemas comuns no monitoramento CaptchaAI + Datadog

Problema Causa Correção
Métricas não aparecem Agente DogStatsD não está em execução Verifique DD_AGENT_HOST; confira docker ps para o contêiner do agente
Histograma de latência vazio Nenhuma solução bem-sucedida foi rastreada Confirme que statsd.histogram() é chamado no caminho de sucesso
Tags faltando Formato de tag incorreto Use o formato key:value, sem espaços dentro da tag
Métricas duplicadas Vários repórteres em execução simultânea Garanta apenas um relator de saldo por implantação

Perguntas frequentes

Preciso de um agente do Datadog em cada worker do pipeline?

Não. Rode um agente DogStatsD por host. Todos os workers desse host enviam as métricas para o agente local, que as encaminha para o intake do Datadog — não é preciso um agente por processo.

Como evito estourar o limite de séries temporais personalizadas no Datadog?

A cardinalidade explode quando você inclui valores de alta variação nas tags — como captcha_id ou IP do usuário. Use apenas tags de baixa cardinalidade (captcha_type, env, region) e deixe identificadores únicos nos logs, não nas tags de métrica.

Esses alertas servem também para Cloudflare Turnstile e GeeTest v3, ou só para reCAPTCHA?

Servem para qualquer tipo suportado pela CaptchaAI. Basta variar a tag captcha_type (turnstile, geetest_v3, recaptcha_v2 etc.) — as métricas captcha.solve.* e os alertas de saldo e fila são agnósticos ao tipo de desafio.

Consigo montar esse painel usando só a API HTTP do Datadog, sem instalar o agente DogStatsD?

Sim, é possível enviar métricas via API HTTP do Datadog, mas o DogStatsD (UDP local) tem menor overhead por chamada e é o caminho recomendado para pipelines de alto volume — a API HTTP costuma valer mais para reportar métricas agregadas em batch, não por solve individual.

Vale a pena combinar isso com o Datadog APM, ou as métricas customizadas já bastam?

As métricas customizadas cobrem a maior parte dos alertas operacionais. O Datadog APM (via ddtrace) soma valor quando você precisa investigar uma chamada lenta específica dentro de uma transação maior — os dois se complementam, não competem.

Artigos relacionados

Próximos passos

Leve observabilidade real ao seu pipeline de CAPTCHA — comece com uma chave de API da CaptchaAI e conecte-a ao Datadog ainda hoje.

Guias relacionados:

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