Solución de Problemas

Errores y soluciones comunes de GeeTest v3

¿Tu integración de GeeTest v3 devuelve errores intermitentes aunque el código no haya cambiado? En la inmensa mayoría de los casos el culpable es uno solo: un valor challenge caducado. GeeTest v3 genera un challenge nuevo cada vez que el widget se carga en la página, y ese valor tiene una vida útil de apenas unos segundos. Si lo capturas una vez y lo reutilizas, la API lo rechaza en el envío o la página de destino invalida la respuesta.

El resto de los fallos de GeeTest v3 se reparte entre tres momentos del flujo: el envío de la tarea a la API, el sondeo del resultado y la validación en la página de destino. La documentación de GeeTest v3 de CaptchaAI es explícita en este punto: necesitas un challenge nuevo para cada solicitud de resolución.

Esta guía recorre cada categoría con el código de error exacto, la causa real y la corrección más rápida, más ejemplos completos en Python y Node.js que ya incorporan la renovación del challenge.


El error más frecuente: un challenge caducado

Si solo vas a comprobar una cosa antes que nada, comprueba la frescura del challenge.

GeeTest v3 depende de dos parámetros clave:

  • gt — la clave pública del sitio (estática, no cambia)
  • challenge — la clave dinámica del desafío (cambia en cada carga de página)

Por qué sucede

El valor challenge se genera en el momento en que el widget de GeeTest se inicializa en la página. Si lo capturas una sola vez y lo reutilizas en varias solicitudes de resolución, cada envío posterior al primero:

  • será rechazado por la API en el momento del envío, o
  • devolverá un resultado que la página de destino rechazará porque el challenge ya expiró

Un ejemplo habitual: un equipo que automatiza las pruebas de QA del login de un panel interno o de un marketplace regional captura el challenge al arrancar el script y lo reutiliza en cada iteración del bucle. La primera resolución funciona y el resto falla en cadena. La corrección es pedir un challenge nuevo dentro del bucle, justo antes de cada envío.

Cómo corregirlo

Antes de cada solicitud de resolución, inspecciona las peticiones de red de la página para localizar la llamada a la API que devuelve un challenge nuevo. Reprodúcela para obtener un valor fresco y envíalo a CaptchaAI de inmediato.

# Pseudocode: fetch a fresh challenge before each solve
import requests

def get_fresh_challenge(target_url):
    """Hit the GeeTest init endpoint to get a new challenge."""
    resp = requests.get(f"{target_url}/geetest/register", timeout=10)
    data = resp.json()
    return data["challenge"], data["gt"]

challenge, gt = get_fresh_challenge("https://example.com")
# Now submit to CaptchaAI immediately — do not delay

Regla práctica: Si pasan más de unos pocos segundos entre la captura del challenge y el envío de la solicitud de resolución, renuévalo.


Errores en el envío a la API (in.php)

Estos fallos aparecen cuando envías la tarea a https://ocr.captchaai.com/in.php. Casi todos son problemas de credenciales o de parámetros que se corrigen antes de volver a lanzar la solicitud.

Código Causa Cómo corregirlo
ERROR_WRONG_USER_KEY El formato de la clave API es incorrecto (debe tener 32 caracteres). Verifica la clave en captchaai.com/api.php; no añadas caracteres extra ni espacios en blanco.
ERROR_KEY_DOES_NOT_EXIST La clave tiene el formato correcto pero no corresponde a ninguna cuenta activa. Inicia sesión en tu panel de CaptchaAI y confirma que la clave esté activa.
ERROR_ZERO_BALANCE No hay threads libres en tu plan actual. Espera a que se liberen threads, reduce la concurrencia o mejora tu plan.
ERROR_PAGEURL Falta el parámetro pageurl en la solicitud. Añade la URL completa de la página donde se carga el widget (ejemplo más abajo).
ERROR_BAD_PARAMETERS Falta uno o varios campos obligatorios, o están mal formados. Revisa la tabla de parámetros requeridos que sigue.
Respuestas HTML o 500/502 Error transitorio del lado del servidor, no un problema de parámetros. Espera de 5 a 10 segundos y reintenta la solicitud.

Para ERROR_PAGEURL, envía la URL exacta de la página, con protocolo y ruta:

pageurl=https://staging.example.com/qa-login

Y estos son los parámetros que in.php exige para GeeTest v3; si falta cualquiera de ellos, recibirás ERROR_BAD_PARAMETERS:

Parámetro Tipo Requerido Descripción
key string Tu clave API de CaptchaAI
method string Debe ser geetest
gt string Clave pública estática del sitio
challenge string Clave dinámica del desafío (debe estar fresca)
pageurl string URL completa de la página

Errores al sondear el resultado (res.php)

Estos fallos aparecen cuando consultas https://ocr.captchaai.com/res.php para recoger el resultado. Ojo: no todos son errores reales. CAPCHA_NOT_READY solo indica que la resolución sigue en curso.

Código Qué significa Cómo actuar
CAPCHA_NOT_READY No es un error: el captcha todavía se está resolviendo. En CaptchaAI, un GeeTest v3 suele resolverse en menos de 12 segundos con una alta tasa de éxito. Espera 5 segundos y vuelve a consultar el resultado; no lo trates como un fallo.
ERROR_WRONG_ID_FORMAT El formato del ID del captcha es incorrecto; los ID deben ser solo numéricos. Usa el ID exacto devuelto por in.php, sin modificarlo.
ERROR_WRONG_CAPTCHA_ID El ID no coincide con ninguna tarea enviada. Confirma que sondeas el ID correcto; si enviaste varias tareas, consulta la que toca.
ERROR_EMPTY_ACTION Falta el parámetro action o está vacío en la consulta. Incluye action=get en cada solicitud (formato más abajo).
ERROR_CAPTCHA_UNSOLVABLE El desafío no se pudo resolver, casi siempre por un challenge caducado o una variante de GeeTest no compatible. Renueva el valor challenge y reintenta.
ERROR_INTERNAL_SERVER_ERROR Problema temporal del lado del servidor en CaptchaAI. Espera 10 segundos y reintenta.

Una consulta correcta a res.php incluye siempre el parámetro action=get:

https://ocr.captchaai.com/res.php?key=YOUR_KEY&action=get&id=CAPTCHA_ID

Cuando la API responde pero la página de destino rechaza la validación

Estos son los fallos más difíciles de depurar, porque la API de CaptchaAI devuelve un resultado válido y aun así la página de destino lo rechaza.

Cuando una resolución de GeeTest v3 tiene éxito, la API devuelve tres valores:

{
  "challenge": "1a2b3456cd67890e12345fab678901c2de",
  "validate": "09fe8d7c6ba54f32e1dcb0a9fedc8765",
  "seccode": "12fe3d4c56789ba01f2e345d6789c012|jordan"
}

Estos valores deben enviarse a la página de destino de esta forma:

Campo de la respuesta API Campo de la página de destino
challenge geetest_challenge
validate geetest_validate
seccode geetest_seccode

Cuando la validación falla pese a un resultado válido de la API, casi siempre es por uno de estos cuatro motivos:

Fallo Síntoma Causa Solución
Mapeo de campos incorrecto La página rechaza los valores de inmediato. Los valores se insertan en los campos o en la ruta de solicitud equivocados. Inspecciona una resolución manual, localiza la petición POST del resultado y haz coincidir exactamente los nombres de tus campos.
challenge caducado en el origen La página dice que el desafío expiró o no es válido. El challenge se capturó demasiado pronto o se reutilizó. Obtén un challenge nuevo justo antes de cada resolución; no lo guardes en caché ni lo reutilices.
Contexto de página incorrecto La validación falla incluso con entradas nuevas. El pageurl no coincide con la página real donde se cargó el widget. Usa la URL exacta, con protocolo y ruta; si el widget se carga por AJAX, usa la URL de esa ruta.
Estructura de solicitud incompatible Los campos son correctos pero el formato es incorrecto. La página espera otro tipo de contenido (cuerpo JSON frente a datos de formulario) u otros campos. Compara con el tráfico de una resolución manual: haz coincidir el tipo de contenido, el orden de los campos y cualquier campo extra.

Cómo evitar la mayoría de estos errores de raíz

Casi todos los fallos anteriores se reducen a dos hábitos. El primero es tratar el challenge como un valor de un solo uso: pídelo dentro del bucle, justo antes de cada envío, y nunca lo conserves entre iteraciones. El segundo es reproducir con exactitud la solicitud que hace el navegador en una resolución manual —mismo pageurl, mismos nombres de campo (geetest_challenge, geetest_validate, geetest_seccode) y mismo tipo de contenido— en lugar de suponer el formato.

Si automatizas la QA de un panel interno o de un marketplace regional, añade un reintento con retroceso exponencial para los códigos transitorios (ERROR_INTERNAL_SERVER_ERROR, 500/502) y trata CAPCHA_NOT_READY como una espera esperada, no como un fallo. Con esos dos hábitos, la mayoría de las integraciones dejan de arrojar errores intermitentes.


Python: resolución completa de GeeTest v3 con challenge nuevo

import time
import requests

API_KEY = "YOUR_CAPTCHAAI_API_KEY"

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


def get_fresh_challenge(target_url):
    """Fetch a fresh GeeTest challenge from the target page."""
    resp = requests.get(f"{target_url}/api/geetest/register", timeout=10)
    data = resp.json()
    return data["gt"], data["challenge"]


def solve_geetest_v3(api_key, gt, challenge, pageurl):
    """Submit a GeeTest v3 challenge and return the validation package."""

    # Submit
    submit_resp = requests.post(
        SUBMIT_URL,
        data={
            "key": api_key,
            "method": "geetest",
            "gt": gt,
            "challenge": challenge,
            "pageurl": pageurl,
            "json": 1,
        },
        timeout=30,
    )
    submit_resp.raise_for_status()
    submit_data = submit_resp.json()

    if submit_data.get("status") != 1:
        raise RuntimeError(f"Submit failed: {submit_data}")

    captcha_id = submit_data["request"]
    print(f"Task created — captcha ID: {captcha_id}")

    # Wait before first poll
    time.sleep(15)

    # Poll for result
    for _ in range(60):
        result_resp = requests.get(
            RESULT_URL,
            params={
                "key": api_key,
                "action": "get",
                "id": captcha_id,
                "json": 1,
            },
            timeout=30,
        )
        result_resp.raise_for_status()
        result_data = result_resp.json()

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

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

        raise RuntimeError(f"Polling error: {result_data}")

    raise TimeoutError("GeeTest v3 solve timed out")


# Usage: always fetch a fresh challenge first
PAGE_URL = "https://staging.example.com/qa-login"
gt, challenge = get_fresh_challenge(PAGE_URL)
result = solve_geetest_v3(API_KEY, gt, challenge, PAGE_URL)
print(f"Result: {result}")

# The result contains: challenge, validate, seccode
# Map them to: geetest_challenge, geetest_validate, geetest_seccode

Node.js: resolución completa de GeeTest v3 con challenge nuevo

const API_KEY = "YOUR_CAPTCHAAI_API_KEY";
const SUBMIT_URL = "https://ocr.captchaai.com/in.php";
const RESULT_URL = "https://ocr.captchaai.com/res.php";

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function getFreshChallenge(targetUrl) {
  const resp = await fetch(`${targetUrl}/api/geetest/register`);
  const data = await resp.json();
  return { gt: data.gt, challenge: data.challenge };
}

async function solveGeetestV3(apiKey, gt, challenge, pageurl) {
  // Submit
  const submitResp = await fetch(SUBMIT_URL, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      key: apiKey,
      method: "geetest",
      gt: gt,
      challenge: challenge,
      pageurl: pageurl,
      json: "1",
    }),
  });

  const submitData = await submitResp.json();
  if (submitData.status !== 1) {
    throw new Error(`Submit failed: ${JSON.stringify(submitData)}`);
  }

  const captchaId = submitData.request;
  console.log(`Task created — captcha ID: ${captchaId}`);

  await sleep(15_000);

  // Poll for result
  for (let i = 0; i < 60; i++) {
    const resultResp = await fetch(
      `${RESULT_URL}?${new URLSearchParams({
        key: apiKey,
        action: "get",
        id: captchaId,
        json: "1",
      })}`
    );

    const resultData = await resultResp.json();

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

    if (resultData.status === 1) {
      return resultData.request;
    }

    throw new Error(`Polling error: ${JSON.stringify(resultData)}`);
  }

  throw new Error("GeeTest v3 solve timed out");
}

// Usage
const PAGE_URL = "https://staging.example.com/qa-login";

(async () => {
  const { gt, challenge } = await getFreshChallenge(PAGE_URL);
  const result = await solveGeetestV3(API_KEY, gt, challenge, PAGE_URL);
  console.log("Result:", result);
  // Map result fields to: geetest_challenge, geetest_validate, geetest_seccode
})();

Preguntas frecuentes

¿Cuánto tiempo sigue siendo válido un challenge de GeeTest v3?

Muy poco: apenas unos segundos. El valor se genera al inicializarse el widget en la página, así que la práctica segura es tratarlo como de un solo uso y obtenerlo justo antes de enviar la tarea a la API. Si tu script tarda en llegar del challenge al envío, renuévalo.

¿Necesito un proxy para resolver GeeTest v3 con la API?

No. GeeTest v3 se resuelve enviando gt, challenge y pageurl a la API; no dependes de un proxy para el flujo básico de resolución. Lo que sí es imprescindible es que el challenge esté fresco y que el pageurl coincida con la página real donde se carga el widget.

¿Cómo distingo un CAPCHA_NOT_READY de un error real?

CAPCHA_NOT_READY no es un fallo: significa que la resolución sigue en curso. La regla es sencilla: ante ese valor, espera 5 segundos y vuelve a consultar res.php; solo los códigos que empiezan por ERROR_ exigen una corrección. Como un GeeTest v3 suele resolverse en menos de 12 segundos, un puñado de reintentos basta.

¿Cuántos threads necesito para resolver GeeTest v3 en volumen?

Depende de cuántas resoluciones simultáneas quieras mantener. CaptchaAI factura por thread concurrente, con resoluciones ilimitadas por thread dentro del mes: el plan BASIC ($15/mes) incluye 5 threads y el plan ADVANCE ($90/mes) sube a 50 threads. Dimensiona los threads según tu pico de concurrencia, no según el total diario de resoluciones.

¿Qué diferencia a GeeTest v3 de reCAPTCHA v2?

GeeTest v3 es un desafío de rompecabezas o deslizador (slider), no una casilla de verificación. Requiere el parámetro dinámico challenge, que debes renovar en cada resolución, y la API devuelve tres campos de validación (challenge, validate, seccode) en lugar de un único token. Para el flujo de reCAPTCHA v2, revisa cómo resolver reCAPTCHA v2 con la API.

¿CaptchaAI es compatible con GeeTest v4?

Este artículo cubre únicamente GeeTest v3. El soporte de GeeTest v4 está anunciado como próximamente y todavía no está disponible. Revisa la documentación de la API de CaptchaAI para conocer los tipos de captcha compatibles en cada momento.


Lista de comprobación para arreglar tu integración

Si tu integración de GeeTest está fallando, recorre estos cuatro puntos en orden:

  1. Verifica el challenge: ¿está fresco? Obtén uno nuevo inmediatamente antes de cada resolución.
  2. Revisa los parámetros: gt, challenge y pageurl deben ser correctos.
  3. Inspecciona el mapeo de campos: los valores devueltos challenge, validate y seccode tienen que ir a los campos correctos de la página.
  4. Compara con una resolución manual: usa las DevTools del navegador para capturar la estructura exacta de la solicitud en una resolución manual de GeeTest que sí funcione.

Empieza con el solver de GeeTest v3 de CaptchaAI, confirma tus parámetros con la documentación de la API y lee cómo resolver GeeTest v3 con la API si necesitas repasar el flujo del challenge.


Registro de iteración

Iteración Enfoque Cambios
Borrador 1 Estructura y contenido Borrador inicial de solución de problemas: 3 etapas de error, tabla de errores y soluciones, preguntas frecuentes
Borrador 2 Precisión técnica Se verificaron todos los códigos de error y los parámetros de GeeTest contra captchaai.com/api-docs. Se añadió la tabla de parámetros de la API. Se confirmó el mapeo challenge/validate/seccode.
Borrador 3 Ejemplos de código Se añadieron ejemplos completos en Python y Node.js con recuperación de un challenge nuevo. Se añadió pseudocódigo para el patrón de renovación del challenge.
Borrador 4 Profundidad en los fallos de validación Se amplió la sección de validación de la página de destino con 4 modos de error distintos. Se añadió la tabla de mapeo de campos y el diagnóstico de estructura de solicitud incompatible.
Borrador 5 Pulido final de control de calidad Se verificó que todos los códigos de error coinciden con la documentación oficial. Se agruparon los errores de envío y de sondeo en tablas de referencia y se enlazaron los artículos del clúster.

Resumen de activos visuales

Imagen de héroe

  • Texto alternativo: Desarrollador depurando errores de GeeTest v3: diagnóstico de fallos de envío, sondeo y validación
  • Debe mostrar: Contexto de depuración con las etapas del flujo de errores y sus puntos de fallo
  • Nombre de archivo: geetest-v3-errors-troubleshooting-hero.png

Visual 1 en el artículo

  • Ubicación: Después de "Errores al sondear el resultado (res.php)"
  • Tipo: Árbol de decisión
  • Texto alternativo: Árbol de decisión para fallos de GeeTest v3: errores de envío, errores de sondeo y fallos de validación
  • Nombre de archivo: geetest-v3-error-decision-tree.png

Visual 2 en el artículo

  • Ubicación: Después de "Cuando la API responde pero la página de destino rechaza la validación"
  • Tipo: Diagrama de causas y soluciones
  • Texto alternativo: Diagrama con las causas habituales del rechazo de la página en GeeTest v3 y sus soluciones
  • Nombre de archivo: geetest-v3-validation-causes-fixes.png

Artículos relacionados

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