Solución de Problemas

Códigos de error CaptchaAI: referencia completa y soluciones

Antes de la lista completa, quédate con una idea: la API de CaptchaAI casi nunca falla "al azar". Cada respuesta con status: 0 dice una de seis cosas — tus credenciales no sirven, tus parámetros o tu archivo no cuadran, tu proxy no llega, el servidor tropezó, ese desafío no salió, o todavía no está listo. La clase, y no el código exacto, decide si reintentas, si corriges y reenvías, o si solo esperas.

Los errores llegan por dos endpoints distintos:

  • in.php — envías la tarea; aquí saltan los problemas de clave, de parámetros y de archivo.
  • res.php — consultas el resultado; aquí saltan los problemas del propio desafío y del proxy durante la resolución.

Si añades json=1 a la solicitud, el error viaja como JSON:

{"status": 0, "request": "ERROR_CODE_HERE"}

Sin json=1 recibes el mismo código en texto plano: ERROR_CODE_HERE. Usa siempre json=1: parsear texto plano es la primera fuente de bugs.


Los códigos de error de CaptchaAI en tres reglas

Patrón de error Qué hacer
CAPCHA_NOT_READY Es normal: vuelve a consultar el resultado a los 5 segundos
Cualquier ERROR_ de parámetro o formato Corrige la solicitud; no reenvíes la misma tarea sin cambios
Errores de servidor (ERROR_SERVER_ERROR, ERROR_INTERNAL_SERVER_ERROR) Reintenta a los 10 segundos con retroceso exponencial

A partir de aquí los códigos van agrupados por esas clases, que es como se depuran en la práctica.


Errores de credenciales y de cuenta

Descártalos primero: si falla la clave o la capacidad de threads, ningún ajuste de parámetros arregla nada.

ERROR_WRONG_USER_KEY

Causa: el parámetro key tiene un formato incorrecto. Las claves API de CaptchaAI tienen exactamente 32 caracteres.

Solución:

  1. Cuenta los caracteres de tu clave: deben ser 32.
  2. Revisa que no arrastre espacios ni saltos de línea.
  3. Cópiala de nuevo desde tu panel de API.

Incorrecto:

{
  "key": "abc123... "
}

Correcto:

{
  "key": "abc12345678901234567890123456789a"
}

ERROR_KEY_DOES_NOT_EXIST

Causa: la clave tiene el formato correcto, pero no corresponde a ninguna cuenta del sistema.

Solución:

  • Inicia sesión en captchaai.com y copia la clave desde tu panel de control.
  • Comprueba que no estés mezclando la clave de una cuenta de pruebas con la de producción.
  • Si acabas de crear la cuenta, espera unos minutos hasta que la clave quede activa.

ERROR_ZERO_BALANCE

Causa: tu cuenta no tiene threads libres para aceptar la tarea.

Solución:

  • Espera a que terminen las tareas en curso; cada una libera su thread al completarse.
  • Sube de plan si tu volumen ya no cabe en la capacidad actual.
  • Revisa el estado de tu cuenta en captchaai.com/api.php.

El nombre engaña. CaptchaAI factura por thread concurrente, no por resolución, y cada plan incluye resoluciones ilimitadas. Este código suele significar "todos tus threads están ocupados ahora mismo". Con BASIC ($15/mes, 5 threads) y cinco tareas en vuelo, la sexta devuelve ERROR_ZERO_BALANCE hasta que una termine; STANDARD ($30/mes, 15 threads) y ADVANCE ($90/mes, 50 threads) amplían ese margen.


IP_BANNED

Causa: tu IP quedó bloqueada temporalmente tras varios intentos de autenticación fallidos seguidos.

Solución: detén el proceso, espera unos 5 minutos y vuelve con las credenciales correctas. Un worker que reintenta en bucle con una clave inválida renueva el bloqueo en cada intento: corta al primer error de clave.


Errores de parámetros del desafío

La clave es válida y hay threads libres, pero la descripción del desafío no cuadra con la página real.

ERROR_PAGEURL

Causa: el parámetro pageurl falta o llega vacío. Es obligatorio en todos los CAPTCHA basados en token (reCAPTCHA, Cloudflare Turnstile, GeeTest v3, etc.).

Solución: envía la URL completa de la página donde se carga el CAPTCHA, protocolo incluido.

Incorrecto:

{
  "pageurl": ""
}

Correcto:

{
  "pageurl": "https://staging.example.com/qa-login"
}

ERROR_WRONG_GOOGLEKEY / ERROR_GOOGLEKEY

Causa: el parámetro googlekey (el sitekey) llega vacío, truncado o con un formato inválido.

Solución:

  • Vuelve a extraer el sitekey del atributo data-sitekey de la página, o del parámetro k de la URL de anclaje de reCAPTCHA.
  • Verifica que el valor no venga cortado: un regex mal anclado suele recortar los últimos caracteres.

Incorrecto:

{
  "googlekey": ""
}

Correcto:

{
  "googlekey": "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-"
}

ERROR_BAD_TOKEN_OR_PAGEURL

Causa: el par googlekey + pageurl no es válido en conjunto. El sitekey existe, pero no está registrado para esa URL.

Causas frecuentes:

  • El widget vive en un iframe de otro subdominio y tú envías la URL de la página contenedora.
  • El sitekey pertenece a otra página o a otro dominio del mismo sitio.
  • Copiaste el sitekey de staging y lo usas contra producción, o al revés.

Solución:

  1. Si el widget vive en un iframe, usa la URL de su atributo src como pageurl.
  2. Toma el sitekey de la misma página que vas a resolver, no de la portada.
  3. Comprueba el par cargando https://www.google.com/recaptcha/api2/anchor?k=YOUR_SITEKEY: si no renderiza el widget, el par está mal.

Un caso típico en portales hispanohablantes. Los portales de cita previa y de trámites públicos — desde los centros de visados BLS hasta sedes electrónicas como la del SAT mexicano — suelen montar el formulario protegido en un iframe con dominio propio, distinto del que ves en la barra del navegador. Si envías la URL exterior, recibes ERROR_BAD_TOKEN_OR_PAGEURL una y otra vez aunque el sitekey sea correcto. Inspecciona el DOM, localiza el iframe real y envía su src. Y respeta siempre los términos de servicio del portal y la normativa de protección de datos aplicable.


ERROR_BAD_PARAMETERS

Causa: faltan parámetros obligatorios del método, o llegan con el tipo de dato equivocado.

Solución: contrasta tu payload con la lista mínima de cada tipo antes de reenviar.

Tipo de CAPTCHA Parámetros obligatorios
reCAPTCHA v2/v3 key, method=userrecaptcha, googlekey, pageurl
Cloudflare Turnstile key, method=turnstile, sitekey, pageurl
Cloudflare Challenge key, method=cloudflare_challenge, pageurl, proxy, proxytype
GeeTest v3 key, method=geetest, gt, challenge, pageurl
BLS key, method=bls, body, textinstructions
Normal/imagen key, method=post, file o body

Un detalle que provoca muchos diagnósticos equivocados: reCAPTCHA usa googlekey y Turnstile usa sitekey. Cruzar ambos nombres devuelve ERROR_BAD_PARAMETERS aunque el valor sea correcto.


ERROR_WRONG_ID_FORMAT

Causa: el ID de la tarea debe ser únicamente numérico y estás enviando otra cosa.

Solución: envía exactamente el ID que devolvió in.php, sin comillas, sin el prefijo OK| y sin espacios. Si parseas texto plano, este error es la señal para pasarte a json=1.


ERROR_WRONG_CAPTCHA_ID

Causa: el ID no existe o ya caducó.

Solución:

  • Confirma que consultas el resultado con el ID de tu propio envío, no con uno guardado de otra ejecución.
  • Los ID caducan pasado cierto tiempo: si la tarea es muy antigua, envíala de nuevo en lugar de insistir.

Errores de imagen y archivo

Solo aparecen en los métodos que suben contenido (method=post, BLS y similares). Cuatro de ellos se diagnostican de un vistazo:

Código Qué pasó Qué hacer
ERROR_TOO_BIG_CAPTCHA_FILESIZE La imagen supera el tamaño máximo permitido. Comprime o redimensiona antes de enviar; recortar el área del desafío suele bastar. JPEG para fotos, PNG para capturas.
ERROR_ZERO_CAPTCHA_FILESIZE El archivo pesa menos de 100 bytes: subida vacía o dañada. Comprueba que envías datos de imagen reales. Suele venir de capturar el elemento antes de que termine de cargarse.
ERROR_WRONG_FILE_EXTENSION La extensión no está admitida; se aceptan jpg, jpeg, png y gif. Convierte la imagen antes de subirla. Renombrar un .webp a .png no sirve: el servidor mira el contenido, no el nombre.
ERROR_IMAGE_TYPE_NOT_SUPPORTED El servidor no logra deducir el tipo de imagen a partir del contenido. Conviértela a PNG o JPEG estándar y comprueba que el archivo no esté dañado.

ERROR_UPLOAD

Causa: el servidor no pudo leer el archivo subido ni el payload base64.

Solución:

  • En subidas de archivo, revisa la codificación de tu formulario multipart.
  • En base64, comprueba que la cadena esté completa y bien codificada.
  • Prueba con una imagen que sepas que funciona.

Errores de proxy

Aparecen al enviar y al consultar el resultado, y comparten diagnóstico: el proxy es tuyo, así que la comprobación empieza fuera de CaptchaAI.

ERROR_BAD_PROXY

Causa: el proxy que indicaste es inaccesible o el sistema lo marcó como defectuoso.

Solución:

  • Pruébalo por tu cuenta: ¿llega ese proxy al sitio de destino desde tu máquina?
  • Prueba con otro proxy antes de tocar nada más en tu código.
  • Revisa el formato: login:password@IP:PORT, o IP:PORT en proxies autenticados por IP.

El uso de proxy debe estar habilitado en tu cuenta. Si nunca lo activaste, escribe al soporte de CaptchaAI antes de seguir depurando.


ERROR_PROXY_CONNECTION_FAILED

Causa: durante la resolución, el solver no consiguió conectarse al sitio de destino a través de tu proxy.

Solución:

  • El proxy puede estar caído de forma pasajera; cambia a otro y reintenta.
  • El sitio de destino puede estar rechazando esa IP de salida.
  • Verifica que el proxy alcance ese dominio concreto, no solo internet en general.

Errores de servidor

ERROR_SERVER_ERROR / ERROR_INTERNAL_SERVER_ERROR

Causa: un fallo pasajero del lado del servidor.

Solución: espera 10 segundos y reintenta con retroceso exponencial. Es la única familia de códigos donde reintentar sin cambiar nada es la respuesta correcta:

import time

retry_delay = 10
for attempt in range(5):
    response = submit_captcha()
    if response.get("status") == 1:
        break
    time.sleep(retry_delay)
    retry_delay *= 2  # 10s, 20s, 40s, 80s, 160s

Sondeo del resultado (res.php)

CAPCHA_NOT_READY

No es un error. Significa que la resolución sigue en curso.

Acción: espera 5 segundos y vuelve a consultar el resultado.

if result.get("request") == "CAPCHA_NOT_READY":
    time.sleep(5)
    continue  # poll again

Guía de tiempos:

Tipo de CAPTCHA Primera consulta a los Intervalo entre consultas
reCAPTCHA v2/v3/Enterprise 15 segundos 5 segundos
Cloudflare Turnstile 15 segundos 5 segundos
Cloudflare Challenge 20 segundos 5 segundos
GeeTest v3 15 segundos 5 segundos
CAPTCHA normal/imagen 5 segundos 5 segundos

Consultar antes de tiempo no acelera nada: solo multiplica solicitudes.


ERROR_CAPTCHA_UNSOLVABLE

Causa: CaptchaAI no consiguió resolver ese desafío concreto después de varios intentos.

Motivos habituales:

  • El tipo de CAPTCHA no es compatible, o los parámetros no describen bien el desafío.
  • El desafío llegó caducado o dañado.
  • En resoluciones con proxy, el proxy es demasiado lento o inestable.
  • El sitio cambió su implementación del CAPTCHA.

Solución:

  1. Revisa sitekey, pageurl y method antes que nada.
  2. Envía una tarea nueva, no el mismo ID.
  3. Si usas proxy, prueba con otro.
  4. Si el error se vuelve constante en un sitio que antes funcionaba, vuelve a extraer sitekey y URL: probablemente movieron el widget.

Un ERROR_CAPTCHA_UNSOLVABLE aislado entre cientos de resoluciones es ruido normal. Vigila la tasa: si se sostiene por encima del 1–2%, el problema está en tus parámetros o en el sitio.


ERROR_EMPTY_ACTION

Causa: falta el parámetro action en la solicitud de sondeo, o llega vacío.

Solución: añade action=get a tu llamada a res.php:

params = {
    "key": api_key,
    "action": "get",  # Required
    "id": captcha_id,
    "json": 1,
}

ERROR_WRONG_USER_KEY / ERROR_KEY_DOES_NOT_EXIST en res.php

También aparecen al consultar el resultado, con la misma causa y solución que en el envío. Si salen aquí y no en in.php, suele ser porque el worker que sondea lee la clave de otra variable de entorno.


Plantilla de manejo de errores

El patrón es el mismo en cualquier lenguaje: dos conjuntos de códigos — los que hay que corregir y los que se reintentan —, un backoff y un bucle de sondeo con límite.

Python

import time
import requests

API_KEY = "YOUR_API_KEY"
SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"

# Errors that should not be retried (fix the request first)
NO_RETRY_ERRORS = {
    "ERROR_WRONG_USER_KEY",
    "ERROR_KEY_DOES_NOT_EXIST",
    "ERROR_PAGEURL",
    "ERROR_WRONG_GOOGLEKEY",
    "ERROR_GOOGLEKEY",
    "ERROR_BAD_TOKEN_OR_PAGEURL",
    "ERROR_BAD_PARAMETERS",
    "ERROR_WRONG_FILE_EXTENSION",
    "ERROR_IMAGE_TYPE_NOT_SUPPORTED",
    "IP_BANNED",
}

# Errors that can be retried
RETRY_ERRORS = {
    "ERROR_ZERO_BALANCE",
    "ERROR_SERVER_ERROR",
    "ERROR_INTERNAL_SERVER_ERROR",
    "ERROR_UPLOAD",
}


def solve_captcha(submit_data, max_retries=3, max_polls=60):
    """Submit and solve a CAPTCHA with full error handling."""

    # Submit with retry logic
    for attempt in range(max_retries):
        resp = requests.post(SUBMIT_URL, data={**submit_data, "json": 1}, timeout=30)
        resp.raise_for_status()
        data = resp.json()

        if data.get("status") == 1:
            captcha_id = data["request"]
            break

        error = data.get("request", "UNKNOWN")

        if error in NO_RETRY_ERRORS:
            raise ValueError(f"Fatal error (fix request): {error}")

        if error in RETRY_ERRORS and attempt < max_retries - 1:
            time.sleep(10 * (2 ** attempt))
            continue

        raise RuntimeError(f"Submit failed: {error}")
    else:
        raise RuntimeError("Submit failed after max retries")

    # Poll for result
    time.sleep(15)

    for _ in range(max_polls):
        resp = requests.get(
            RESULT_URL,
            params={"key": API_KEY, "action": "get", "id": captcha_id, "json": 1},
            timeout=30,
        )
        data = resp.json()

        if data.get("request") == "CAPCHA_NOT_READY":
            time.sleep(5)
            continue

        if data.get("status") == 1:
            return data["request"]

        error = data.get("request", "UNKNOWN")
        if error == "ERROR_CAPTCHA_UNSOLVABLE":
            raise RuntimeError("CAPTCHA unsolvable — resubmit with fresh parameters")

        raise RuntimeError(f"Poll error: {error}")

    raise TimeoutError("Solve timed out")

Node.js

const NO_RETRY_ERRORS = new Set([
  "ERROR_WRONG_USER_KEY",
  "ERROR_KEY_DOES_NOT_EXIST",
  "ERROR_PAGEURL",
  "ERROR_WRONG_GOOGLEKEY",
  "ERROR_BAD_TOKEN_OR_PAGEURL",
  "ERROR_BAD_PARAMETERS",
  "IP_BANNED",
]);

async function solveCaptcha(submitData, maxRetries = 3, maxPolls = 60) {
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

  // Submit with retry
  let captchaId;
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const resp = await fetch("https://ocr.captchaai.com/in.php", {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: new URLSearchParams({ ...submitData, json: "1" }),
    });
    const data = await resp.json();

    if (data.status === 1) {
      captchaId = data.request;
      break;
    }

    if (NO_RETRY_ERRORS.has(data.request)) {
      throw new Error(`Fatal error: ${data.request}`);
    }

    if (attempt < maxRetries - 1) {
      await sleep(10_000 * 2 ** attempt);
      continue;
    }

    throw new Error(`Submit failed: ${data.request}`);
  }

  // Poll for result
  await sleep(15_000);

  for (let i = 0; i < maxPolls; i++) {
    const resp = await fetch(
      `https://ocr.captchaai.com/res.php?${new URLSearchParams({
        key: submitData.key,
        action: "get",
        id: captchaId,
        json: "1",
      })}`
    );
    const data = await resp.json();

    if (data.request === "CAPCHA_NOT_READY") {
      await sleep(5_000);
      continue;
    }

    if (data.status === 1) return data.request;

    throw new Error(`Poll error: ${data.request}`);
  }

  throw new Error("Solve timed out");
}

Preguntas frecuentes

¿ERROR_ZERO_BALANCE significa que me quedé sin saldo?

Casi nunca. CaptchaAI cobra por threads concurrentes con resoluciones ilimitadas, así que lo normal es que todos tus threads estén ocupados en ese instante. Pon una cola con límite de concurrencia igual a tus threads; si aun así se repite en horas punta, tu volumen pide el siguiente plan.

¿Cuánto debo esperar antes de la primera consulta a res.php?

Entre 5 y 20 segundos según el tipo: 5 segundos para CAPTCHA de imagen, 15 para reCAPTCHA, Turnstile y GeeTest v3, y 20 para Cloudflare Challenge. Después, un intervalo fijo de 5 segundos.

¿Puedo reintentar el mismo ID después de ERROR_CAPTCHA_UNSOLVABLE?

No. Ese ID ya está cerrado y devolverá siempre lo mismo. Envía una tarea nueva con parámetros recién extraídos; si se repite en el mismo sitio, revisa el sitekey y la pageurl.

¿Qué hago si recibo IP_BANNED en producción?

Detén los reintentos. El bloqueo dura unos 5 minutos, pero se renueva con cada intento fallido, así que un worker en bucle puede quedarse bloqueado indefinidamente. Corrige la clave y arranca de nuevo con un solo proceso de prueba.

¿Cambian los códigos si no uso json=1?

No: el código es el mismo, cambia el envoltorio. Con json=1 recibes {"status": 0, "request": "..."} y en texto plano solo la cadena. Trabaja con JSON y te ahorras el parseo frágil que produce falsos ERROR_WRONG_ID_FORMAT.


Guías relacionadas

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