Si tu tasa de resolución cae a las tres de la mañana, quieres enterarte por una alerta en tu teléfono, no por un cliente enfadado al día siguiente. La respuesta corta es instrumentar tu solver: Prometheus recoge las métricas, Grafana las dibuja en un panel y las reglas de alerta te avisan cuando algo se sale de rango. Con menos de cien líneas de Python ya tienes visibilidad completa sobre cuántos CAPTCHA resuelves, cuánto tardan y cuánto saldo te queda.
Este es el patrón que usan los equipos que corren resolución de CAPTCHA en producción, y encaja igual de bien si automatizas la extracción de datos de un marketplace regional que si monitorizas un portal de cita previa protegido por reCAPTCHA.
Antes de empezar: lo que necesitas
El montaje asume tres cosas en tu máquina o servidor:
- Docker y Docker Compose instalados, para levantar todo el stack de una sola vez.
- Una clave API de CaptchaAI activa (el plan BASIC basta para empezar).
- Python 3.9 o superior con la biblioteca
prometheus_client.
Qué métricas vale la pena vigilar
Antes de escribir código, decide qué quieres medir. Con seis métricas cubres casi todas las preguntas operativas del día a día:
- ¿Está resolviendo? Volumen total y resoluciones exitosas.
- ¿Va rápido? Distribución del tiempo de resolución.
- ¿Falla mucho? Errores desglosados por código.
- ¿Me queda saldo? Saldo de la cuenta y cola pendiente.
Estas son las seis métricas que exponemos:
| Métrica | Tipo | Propósito |
|---|---|---|
captcha_solves_total |
Counter | Total de intentos de resolución |
captcha_solves_success |
Counter | Resoluciones exitosas |
captcha_solves_errors |
Counter | Resoluciones fallidas (por código de error) |
captcha_solve_duration |
Histogram | Distribución del tiempo de resolución |
captcha_balance |
Gauge | Saldo actual de la cuenta |
captcha_queue_length |
Gauge | Tareas pendientes en cola |
La distinción entre Counter y Gauge importa: un Counter solo sube (y se reinicia al reiniciar el proceso, por eso siempre lo consultas con rate()), mientras que un Gauge sube y baja, ideal para el saldo o la longitud de la cola.
Exportador de métricas en Python
El corazón del sistema es un envoltorio alrededor de tu solver que incrementa los contadores y observa la duración en cada llamada. Fíjate en la etiqueta method: gracias a ella podrás desglosar cada métrica por tipo de CAPTCHA (reCAPTCHA v2, Turnstile, GeeTest v3) sin duplicar código. El servidor HTTP arranca en el puerto 8000 y expone todo en /metrics.
# metrics.py
import time
import requests
from prometheus_client import (
Counter, Histogram, Gauge, start_http_server,
)
# Define metrics
SOLVES_TOTAL = Counter(
"captcha_solves_total",
"Total CAPTCHA solve attempts",
["method"],
)
SOLVES_SUCCESS = Counter(
"captcha_solves_success",
"Successful CAPTCHA solves",
["method"],
)
SOLVES_ERRORS = Counter(
"captcha_solves_errors",
"Failed CAPTCHA solves",
["method", "error_code"],
)
SOLVE_DURATION = Histogram(
"captcha_solve_duration_seconds",
"CAPTCHA solve duration in seconds",
["method"],
buckets=[5, 10, 15, 20, 30, 45, 60, 90, 120],
)
BALANCE = Gauge(
"captcha_balance_usd",
"Current CaptchaAI account balance in USD",
)
QUEUE_LENGTH = Gauge(
"captcha_queue_length",
"Number of pending CAPTCHA tasks",
)
class InstrumentedSolver:
"""Solver with Prometheus metric instrumentation."""
def __init__(self, api_key):
self.api_key = api_key
self.base = "https://ocr.captchaai.com"
def solve(self, method, **params):
"""Solve CAPTCHA with metric collection."""
SOLVES_TOTAL.labels(method=method).inc()
start = time.time()
try:
token = self._do_solve(method, params)
duration = time.time() - start
SOLVES_SUCCESS.labels(method=method).inc()
SOLVE_DURATION.labels(method=method).observe(duration)
return token
except Exception as e:
error_code = str(e)[:30]
SOLVES_ERRORS.labels(
method=method, error_code=error_code,
).inc()
raise
def update_balance(self):
"""Fetch and update balance metric."""
resp = requests.get(f"{self.base}/res.php", params={
"key": self.api_key,
"action": "getbalance",
"json": 1,
}, timeout=15)
balance = float(resp.json()["request"])
BALANCE.set(balance)
return balance
def _do_solve(self, method, params, timeout=120):
data = {"key": self.api_key, "method": method, "json": 1}
data.update(params)
resp = requests.post(
f"{self.base}/in.php", data=data, timeout=30,
)
result = resp.json()
if result.get("status") != 1:
raise RuntimeError(result.get("request"))
task_id = result["request"]
start = time.time()
while time.time() - start < timeout:
time.sleep(5)
resp = requests.get(f"{self.base}/res.php", params={
"key": self.api_key,
"action": "get",
"id": task_id,
"json": 1,
}, timeout=15)
data = resp.json()
if data["request"] != "CAPCHA_NOT_READY":
if data.get("status") == 1:
return data["request"]
raise RuntimeError(data["request"])
raise TimeoutError("Solve timeout")
# Start metrics server on port 8000
start_http_server(8000)
print("Metrics server running on :8000/metrics")
Acuérdate de llamar a update_balance() de forma periódica (por ejemplo, en un hilo que corra cada minuto); de lo contrario el Gauge del saldo se quedará congelado en su último valor.
Configuración de Prometheus
Prometheus funciona al revés de lo que muchos esperan: no recibe métricas, va a buscarlas. Aquí le decimos que consulte tu solver cada 10 segundos. Un intervalo más corto da más resolución temporal, pero también más carga; para resolución de CAPTCHA, entre 10 y 15 segundos es un punto de equilibrio sensato.
# prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: "captcha-solver"
static_configs:
- targets: ["solver-app:8000"]
scrape_interval: 10s
Stack completo con Docker Compose
Para tener el solver, Prometheus y Grafana levantados con un solo comando, este docker-compose.yml los conecta en la misma red. La contraseña de Grafana es solo para arranque local: cámbiala antes de exponer nada fuera de tu máquina.
# docker-compose.yml
version: "3.8"
services:
solver:
build: .
environment:
- CAPTCHAAI_KEY=${CAPTCHAAI_KEY}
ports:
- "8000:8000"
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana-data:/var/lib/grafana
volumes:
grafana-data:
Consultas PromQL para tu panel de Grafana
Con las métricas fluyendo, estas cinco consultas cubren el panel base. Cópialas tal cual al crear cada panel en Grafana y ajusta la ventana [5m] según el volumen que muevas.
Tasa de éxito
rate(captcha_solves_success[5m])
/ rate(captcha_solves_total[5m]) * 100
Tiempo medio de resolución
rate(captcha_solve_duration_seconds_sum[5m])
/ rate(captcha_solve_duration_seconds_count[5m])
Errores por tipo
sum by (error_code) (
rate(captcha_solves_errors[5m])
)
Saldo a lo largo del tiempo
captcha_balance_usd
Duración P95 de la resolución
histogram_quantile(0.95,
rate(captcha_solve_duration_seconds_bucket[5m])
)
El P95 es más honesto que la media: te dice qué experimenta el peor 5 % de tus solicitudes, que suele ser justo lo que dispara los tiempos de espera aguas abajo.
Reglas de alerta que sí sirven
Un panel bonito no despierta a nadie. Estas tres reglas cubren los fallos que de verdad importan: quedarte sin saldo, una racha de errores y una degradación de la latencia. Ajusta los umbrales a tu realidad antes de ponerlas en producción.
# alert_rules.yml
groups:
- name: captcha-alerts
rules:
- alert: LowBalance
expr: captcha_balance_usd < 5
for: 5m
labels:
severity: warning
annotations:
summary: "CaptchaAI balance below $5"
- alert: HighErrorRate
expr: |
rate(captcha_solves_errors[5m])
/ rate(captcha_solves_total[5m]) > 0.1
for: 10m
labels:
severity: critical
annotations:
summary: "CAPTCHA error rate above 10%"
- alert: SlowSolveTime
expr: |
histogram_quantile(0.95,
rate(captcha_solve_duration_seconds_bucket[5m])
) > 60
for: 15m
labels:
severity: warning
annotations:
summary: "P95 solve time exceeds 60s"
Un caso concreto: agencia con picos nocturnos
Imagina una agencia de datos en Madrid que extrae precios de un marketplace latinoamericano durante la madrugada, cuando la carga es baja. El volumen se dispara de 2:00 a 5:00 y el resto del día es plano. Sin observabilidad, un cambio en el CAPTCHA del sitio objetivo pasaría inadvertido hasta que el informe de la mañana llegara vacío.
Con este stack, la alerta HighErrorRate salta a los diez minutos y el panel muestra, por la etiqueta error_code, si el problema es de red, de saldo o de un tipo de CAPTCHA que empezó a fallar. Como CaptchaAI factura por thread concurrente con resoluciones ilimitadas —el plan BASIC cuesta $15/mes con 5 threads y escala hasta VIP-3 a $7,500/mes con 5.000 threads—, el coste mensual en USD es predecible y encaja bien con equipos que facturan a sus clientes en monedas locales volátiles.
Solución de problemas
| Problema | Causa | Solución |
|---|---|---|
No aparecen métricas en /metrics |
El servidor no arrancó | Llama a start_http_server(8000) |
| Prometheus marca el target como "down" | Dirección de destino incorrecta | Revisa la red y el puerto en Docker |
| Grafana no muestra datos | Falta Prometheus como fuente de datos | Añade la fuente de datos de Prometheus en Grafana |
| Las métricas se reinician al reiniciar | Comportamiento esperado del Counter |
Usa rate(), nunca contadores en crudo |
Preguntas frecuentes
¿Cada cuánto conviene hacer scrape sin sobrecargar el solver?
Entre 10 y 15 segundos es lo habitual. La biblioteca prometheus_client añade menos de 1 ms por operación, así que la instrumentación es prácticamente gratis; el coste real está en el intervalo de recolección, y a ese ritmo el impacto sobre el solver es imperceptible.
¿Cómo separo las métricas por tipo de CAPTCHA?
Gracias a la etiqueta method. Como el exportador la aplica a cada contador e histograma, en Grafana puedes filtrar o agrupar por method para comparar, por ejemplo, la tasa de éxito de reCAPTCHA v2 frente a la de Cloudflare Turnstile sin tocar el código.
¿Puedo recibir una alerta antes de quedarme sin saldo?
Sí. La regla LowBalance vigila el Gauge captcha_balance_usd y avisa cuando baja de un umbral (5 dólares en el ejemplo). Sube ese número si tu consumo por hora es alto, de modo que la alerta te dé margen para recargar antes de que se detengan las resoluciones.
¿Funciona con workers repartidos en varias máquinas?
Sí. Cada worker expone su propio endpoint /metrics y Prometheus recolecta todos los destinos que declares en scrape_configs. Grafana agrega los datos de todas las instancias de forma automática, así que ves el conjunto sin trabajo extra.
Guías relacionadas
Observabilidad de punta a punta: monitoriza CaptchaAI con Prometheus y duerme tranquilo.