DevOps y Escalado

Monitoreo de CaptchaAI con Datadog: métricas y alertas

Para saber si tu proceso de resolución de CAPTCHA está sano necesitas vigilar tres señales en tiempo real:

  • Rendimiento — cuántas tareas se resuelven y cuántas fallan, desglosadas por tipo de error.
  • Latencia — cuánto tarda cada resolución, medida en percentiles (p50, p95, p99).
  • Saldo — cuánto crédito de la API te queda antes de que el pipeline se detenga.

Datadog reúne esas tres señales en un mismo panel, dispara alertas antes de que la cola se atasque o la cuenta se quede sin fondos, y te deja diagnosticar caídas sin abrir logs. Esta guía es un recorrido práctico: qué métricas emitir, cómo enviarlas con DogStatsD desde Python y Node.js, cómo armar el dashboard y qué alertas definir.

Piensa en un equipo de scraping en Ciudad de México que resuelve decenas de miles de reCAPTCHA v2 al día para el QA de su flujo de checkout. Cuando la latencia p95 se dispara o la tasa de errores sube, quieren enterarse por una alerta, no por un cliente. Y como CaptchaAI factura por thread con un costo mensual predecible en USD, vigilar el saldo evita que un pico de volumen frene el pipeline a mitad de una corrida.

Qué métricas de CaptchaAI conviene vigilar

Empieza por siete métricas: describen la salud del pipeline sin saturarte de series temporales. Cada una lleva su tipo de Datadog y la razón por la que importa. Conviene entender los tres tipos antes de instrumentar:

  • Counter — acumula eventos (tareas enviadas, éxitos, errores); Datadog los suma por intervalo.
  • Gauge — una foto del valor actual (saldo, cola, workers vivos) en cada reporte.
  • Histogram — una distribución de la que Datadog deriva percentiles, ideal para la latencia.
Métrica Tipo Por qué importa
captcha.solve.count Counter Total de tareas enviadas
captcha.solve.success Counter Resoluciones correctas
captcha.solve.error Counter Resoluciones fallidas (por tipo de error)
captcha.solve.latency Histogram Tiempo desde el envío hasta la solución
captcha.queue.depth Gauge Tareas pendientes en la cola
captcha.balance Gauge Saldo restante de la API
captcha.worker.active Gauge Procesos worker activos

Enviar métricas desde Python con DogStatsD

El siguiente decorador envuelve tu función de resolución y emite las métricas sin ensuciar la lógica de negocio: cuenta cada envío, marca los éxitos y errores, y registra la latencia solo en el camino correcto. Los reportes de saldo, cola y workers viven en funciones aparte que puedes llamar desde un scheduler.

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)

Instrumentar el pipeline en Node.js

Si tu integración corre en Node.js, hot-shots cumple el mismo papel que la librería de Datadog en Python. El prefijo captcha. y las etiquetas globales de entorno se configuran una sola vez, y a partir de ahí cada resolución reporta count, success/error y latencia con las mismas convenciones que en el ejemplo anterior.

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 };

Dashboard de Datadog listo para importar

Con las métricas fluyendo, arma el panel. Importa esta plantilla JSON en Datadog para levantar un dashboard de monitoreo del pipeline CAPTCHA con la tasa de resolución, los percentiles de latencia, el saldo y la profundidad de cola:

{
  "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{*}"}]
      }
    }
  ]
}

Alertas que se disparan antes de que falle el pipeline

Un dashboard te dice qué pasó; una alerta te avisa a tiempo. Define estos umbrales para que el saldo bajo, un pico de latencia o un worker caído lleguen a tu canal antes de que afecten la producción.

Alerta Condición Gravedad
Saldo bajo captcha.balance < 10 Advertencia
Saldo crítico captcha.balance < 2 Crítica
Tasa de error alta Tasa de error > 10 % en 5 minutos Advertencia
Pico de latencia Latencia p95 > 120 s durante 10 minutos Advertencia
Cola atascada Profundidad de cola > 100 y creciendo durante 5 min Advertencia
Worker caído captcha.worker.active == 0 Crítica

Del umbral al monitor de Datadog

Cada fila de la tabla se traduce a un monitor. Este es el de saldo bajo, listo para crear vía la API de Datadog; replica el mismo patrón cambiando la query y el umbral para las demás alertas:

# 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

Diagnóstico de problemas comunes

Si las métricas no llegan o los gráficos salen vacíos, casi siempre es una de estas cuatro causas:

Problema Causa Solución
Las métricas no aparecen El agente DogStatsD no está corriendo Revisa DD_AGENT_HOST y comprueba con docker ps que el contenedor del agente esté activo
Histograma de latencia vacío No se registró ninguna resolución exitosa Verifica que statsd.histogram() se llame en el camino de éxito
Faltan etiquetas Formato de etiqueta incorrecto Usa el formato key:value; sin espacios en las etiquetas
Métricas duplicadas Varios reporters corriendo a la vez Deja un único reporter de saldo por despliegue

Preguntas frecuentes

¿Qué alerta debo configurar primero?

La de saldo. Un captcha.balance en cero detiene todas las resoluciones de golpe, así que la alerta de saldo bajo (< 10) es la que más incidentes evita. Después, la de tasa de error, que suele ser el primer síntoma de un problema en el flujo.

¿Cada cuánto conviene consultar el saldo de la API?

Cada minuto es un buen punto de partida, como en el ejemplo de Node.js (setInterval(reportBalance, 60000)). El saldo cambia despacio, así que sondearlo con más frecuencia solo añade llamadas a la API sin darte información nueva.

¿Sirve este monitoreo para cualquier tipo de CAPTCHA que resuelva CaptchaAI?

Sí. Las métricas se basan en el patrón de envío y sondeo, no en un tipo concreto, así que instrumentas igual reCAPTCHA v2 y v3, Cloudflare Turnstile o GeeTest v3. Basta con cambiar la etiqueta captcha_type para desglosar la latencia y los errores por tipo.

¿Puedo enviar estas métricas a Prometheus en lugar de Datadog?

Sí. El mismo enfoque de instrumentar la función de resolución aplica con un cliente de Prometheus, usando sus tipos histogram y counter; tienes el equivalente con Prometheus y Grafana enlazado más abajo, en las guías relacionadas.

Artículos relacionados


Conecta CaptchaAI a Datadog

Consigue visibilidad completa de tu pipeline CAPTCHA: empieza con una clave API de CaptchaAI y conéctala a Datadog en minutos.

Guías relacionadas:

Los comentarios están deshabilitados para este artículo.