Cuando la tasa de éxito cae de un día para otro, la causa está casi siempre en una de tres capas: la API no devuelve token, lo devuelve y el sitio lo rechaza, o el sitio cambió y sigues enviando parámetros que ya no existen. Distinguirlas toma quince minutos, porque cada capa deja una huella distinta en tus logs.
El error clásico es reaccionar antes de medir: subes reintentos, cambias de plan y la tasa sigue igual, porque el problema era un sitekey que el equipo de frontend rotó. El orden aquí es el inverso: mide, clasifica y corrige.
Antes de tocar nada: separa el síntoma de la causa
Tareas enviadas frente a tokens recibidos mide tu integración con la API; tokens recibidos frente a formularios aceptados mide la compatibilidad con el sitio. La mayoría de los reportes de soporte confunden la segunda con la primera: si llegan tokens con normalidad, ningún cambio de plan moverá nada.
Caso frecuente en equipos de habla hispana: un flujo de QA sobre el formulario de un portal de cita previa funciona meses y de golpe falla por las tardes. Hay tokens sin errores, así que no es la API. Había cambiado el pageurl: el portal empezó a redirigir a un subdominio regional y el token se resolvía contra un dominio pero se enviaba a otro.
Diagrama de flujo de diagnóstico
Recórrelo de arriba abajo y detente en la primera rama que coincida con tus logs.
Success rate dropped
│
├── Are tokens being generated?
│ ├── NO → Check API errors
│ │ ├── ERROR_WRONG_GOOGLEKEY → Sitekey changed. Re-extract.
│ │ ├── ERROR_BAD_PARAMETERS → Check required params
│ │ ├── ERROR_NO_SLOT → Retry with backoff
│ │ └── Other errors → See error decision tree
│ │
│ └── YES → Tokens generated but rejected by target site
│ │
│ ├── Token expired before use?
│ │ └── YES → Submit token faster (< 60-120s)
│ │
│ ├── Token used for wrong domain?
│ │ └── YES → Check pageurl matches submission domain
│ │
│ ├── reCAPTCHA v3 score too low?
│ │ └── YES → Check action parameter, attach cookies/UA/proxy
│ │
│ ├── Site changed CAPTCHA type?
│ │ └── YES → Re-detect CAPTCHA type
│ │
│ └── Site added additional checks?
│ └── YES → Check for fingerprinting, cookies, headers
Paso 1: mide tu tasa real antes de cambiar nada
Instrumenta el flujo y deja correr una hora de tráfico normal, separando el resultado por método. Una caída que afecta solo a userrecaptcha y deja intacto turnstile ya reduce el espacio de búsqueda.
import requests
import time
from collections import defaultdict
class SuccessTracker:
"""Track solve success rates over time."""
def __init__(self):
self.stats = defaultdict(lambda: {"attempts": 0, "success": 0, "errors": defaultdict(int)})
def record(self, method, success, error_code=None):
self.stats[method]["attempts"] += 1
if success:
self.stats[method]["success"] += 1
elif error_code:
self.stats[method]["errors"][error_code] += 1
def report(self):
for method, data in self.stats.items():
rate = data["success"] / data["attempts"] * 100 if data["attempts"] > 0 else 0
print(f"\n{method}:")
print(f" Attempts: {data['attempts']}")
print(f" Success: {data['success']} ({rate:.1f}%)")
if data["errors"]:
print(" Errors:")
for err, count in sorted(data["errors"].items(), key=lambda x: -x[1]):
print(f" {err}: {count}")
tracker = SuccessTracker()
Guarda el reporte antes y después de cada corrección: sin línea base no sabrás si el cambio ayudó o si coincidió con menos carga.
Paso 2: clasifica el fallo en una de las dos categorías
Categoría A: fallos a nivel de la API
La API de CaptchaAI devuelve un error en lugar de un token. El código nombra la causa: ERROR_WRONG_GOOGLEKEY es un sitekey cambiado, ERROR_BAD_PARAMETERS un campo obligatorio ausente y ERROR_NO_SLOT una señal de reintentar con retroceso exponencial. Lanza una tanda corta de pruebas y cuenta patrones en vez de mirar errores sueltos:
def diagnose_api_failures(api_key, method, params, attempts=10):
"""Run test solves and collect error patterns."""
errors = defaultdict(int)
successes = 0
for i in range(attempts):
try:
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": api_key, "method": method, "json": 1, **params,
}, timeout=30)
result = resp.json()
if result.get("status") != 1:
errors[result.get("request", "UNKNOWN")] += 1
continue
task_id = result["request"]
# Quick poll
time.sleep(15)
resp = requests.get("https://ocr.captchaai.com/res.php", params={
"key": api_key, "action": "get",
"id": task_id, "json": 1,
}, timeout=15)
data = resp.json()
if data.get("status") == 1:
successes += 1
else:
errors[data.get("request", "POLL_ERROR")] += 1
except Exception as e:
errors[f"EXCEPTION:{type(e).__name__}"] += 1
time.sleep(2)
print(f"\nResults: {successes}/{attempts} success")
for err, count in sorted(errors.items(), key=lambda x: -x[1]):
print(f" {err}: {count}")
Si ocho de diez intentos devuelven el mismo código, ya tienes la causa raíz. Si se reparten entre cinco, sospecha de tu capa de red antes que del servicio.
Categoría B: rechazo del token
CaptchaAI devuelve un token válido y el sitio lo rechaza. Es la categoría que más tiempo consume porque no genera errores visibles: todo parece bien hasta que falla la validación. Causas comunes:
| Causa | Qué comprobar |
|---|---|
| El token caducó | Se usó más de 120 s tras generarlo |
| El dominio no coincide | pageurl distinto del dominio de envío |
| Puntuación v3 baja | El sitio exige 0.7 y obtienes 0.3 |
Falta action |
reCAPTCHA v3 exige acción coincidente |
| El sitio cambió | Cambió el sitekey o la estructura |
Paso 3: aplica la corrección que corresponde
Corrección: caducidad del token
Un token no es almacenable. Resuélvelo y úsalo en la misma ejecución, sin colas ni caché intermedias.
def solve_and_use_immediately(api_key, sitekey, pageurl):
"""Solve and use token as fast as possible."""
# Submit
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": api_key,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"json": 1,
}, timeout=30)
task_id = resp.json()["request"]
# Poll aggressively
for _ in range(24):
time.sleep(5)
resp = requests.get("https://ocr.captchaai.com/res.php", params={
"key": api_key, "action": "get",
"id": task_id, "json": 1,
}, timeout=15)
data = resp.json()
if data.get("status") == 1:
token = data["request"]
# USE IMMEDIATELY — don't store for later
submit_form(token)
return True
return False
Corrección: sitekey obsoleto
Si el sitio rota su sitekey, cualquier valor fijo en tu configuración caduca en silencio. Extráelo en cada ejecución.
def solve_with_fresh_params(api_key, pageurl):
"""Re-extract sitekey before each solve."""
import re
resp = requests.get(pageurl, timeout=15)
match = re.search(r'data-sitekey="([^"]+)"', resp.text)
if not match:
raise RuntimeError("Could not find sitekey")
sitekey = match.group(1)
# Now solve with fresh sitekey
# ...
Corrección: parámetro action en reCAPTCHA v3
En reCAPTCHA v3 la acción forma parte de la validación: si el sitio usa submit y tú envías otra, el token se genera pero el sitio lo descarta.
# Check what action the site uses
# Look for: grecaptcha.execute('sitekey', {action: 'submit'})
data = {
"key": api_key,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"version": "v3",
"action": "submit", # Must match site's action
"json": 1,
}
Umbrales de alerta por tipo de CAPTCHA
CaptchaAI mantiene una alta tasa de éxito en los tipos compatibles y sus velocidades publicadas son techos de servicio, no promedios. Vigila la desviación frente a tu línea base:
| Tipo CAPTCHA | Velocidad de referencia | Cuándo investigar |
|---|---|---|
| reCAPTCHA v2 | <60 s |
Caída sostenida frente a tu base |
| reCAPTCHA v3 | <4 s |
Rechazos en una sola action |
| Cloudflare Turnstile | <10 s |
Más tiempo y más rechazos |
| GeeTest v3 | <12 s |
Cambio tras un despliegue |
| BLS | <1 s |
Fallos por franja horaria |
| Imagen/OCR | <0.5 s |
Cambio en la imagen origen |
CaptchaFox (beta), Friendly Captcha (beta) y Lemin (beta) no tienen métricas publicadas: compáralas solo contra tus mediciones. hCaptcha y FunCaptcha (Arkose Labs) no son compatibles y GeeTest v4 figura como próximamente; si tu flujo se topa con alguno, es un cambio de tipo en el sitio, no de configuración.
Síntomas frecuentes y su corrección
| Síntoma | Causa probable | Qué hacer |
|---|---|---|
| La tasa cayó del 98% al 70% de golpe | Cambió el sitekey o la página |
Reextrae los parámetros |
| Todos los tokens v3 rechazados | action incorrecta |
Cópiala del código de la página |
| Los tokens llegan pero caducan | Demasiado tiempo hasta el envío | Envía antes de 60 segundos |
| La tasa varía según la hora | Límite de solicitudes del sitio | Separa más los envíos |
| Solo falla un método | Cambio en ese tipo de CAPTCHA | Vuelve a detectar el tipo |
Cuánta capacidad necesitas mientras diagnosticas
Cada tanda de prueba ocupa capacidad que tu flujo en producción también quiere. Como CaptchaAI factura por thread concurrente y no por resolución, diagnosticar no mueve la factura: lo que compites es la concurrencia. Con BASIC ($15/mes, 5 threads) conviene pausar el flujo mientras mides; desde ADVANCE ($90/mes, 50 threads) puedes reservar threads para pruebas. Los precios siguen en USD, un costo predecible para equipos que facturan en monedas volátiles.
Preguntas frecuentes
¿Cómo sé si el problema es de CaptchaAI o del sitio de destino?
Mira si recibes token. Si llega y el formulario falla igual, el problema está en cómo lo usas o en algo que cambió el sitio. Si llega un código de error, ve a la categoría A.
¿Cuánto tiempo debo medir antes de concluir que hay una caída real?
Al menos una hora de tráfico normal, comparando contra tu línea base por método. Veinte resoluciones no separan una caída del ruido.
¿Sirve de algo subir de plan cuando cae la tasa de éxito?
Solo si el patrón apunta a falta de threads: colas largas, reintentos por saturación. Un plan mayor añade concurrencia; no corrige un sitekey obsoleto.
¿Debo reportar las resoluciones incorrectas?
Sí. Usa el endpoint reportbad para marcarlas: alimenta la mejora de precisión y te deja un registro fechado de la degradación.
¿Por qué la tasa empeora siempre a la misma hora?
Suele ser límite de solicitudes del sitio de destino, no del solver. Reparte los envíos, añade retroceso exponencial y compara la misma franja de dos días antes de tocar nada.
Guías relacionadas
Mide primero, corrige después. Empieza con CaptchaAI.