Tutoriales de API

Cómo resolver GeeTest v3 usando API

GeeTest v3 no te da un sitekey fijo que puedas copiar una vez y olvidar. Cada resolución arranca con dos valores que hay que sacar de la página en vivo: gt, que identifica al sitio y no cambia, y challenge, que se emite por sesión, caduca y es de un solo uso. Ahí se rompen la mayoría de las integraciones: en el challenge reciclado, no en el rompecabezas.

Lo que sigue es mecánico. Envías ese par a la API de CaptchaAI, sondeas hasta que la tarea termine y recibes tres campos para adjuntar al formulario del sitio:

  • geetest_challenge — el challenge ya validado.
  • geetest_validate — la firma que prueba la resolución.
  • geetest_seccode — el código de seguridad que verifica el backend.

Abajo tienes el flujo completo con código en Python y Node.js, la tabla de errores y los tramos de threads.

GeeTest v3 es un tipo compatible en CaptchaAI, con un tiempo de resolución de menos de 12 segundos y una alta tasa de éxito. GeeTest v4 todavía no está disponible (figura como próximamente), así que confirma qué versión carga el sitio antes de escribir una línea.


Antes de escribir código

Un caso habitual en la región: una agencia en Bogotá o Ciudad de México corre de madrugada las pruebas de regresión de su propio portal de acceso, o vigila un trámite público protegido con GeeTest. Cada ejecución choca con el desafío y, sin resolución automática, la suite se queda parada hasta la mañana siguiente.

Con BASIC ($15/mes, 5 threads) resuelves cinco desafíos en paralelo y sin tope de solves en el mes: un costo predecible en USD en vez de una factura variable por resolución, algo que pesa cuando facturas en una moneda local volátil. Automatiza solo flujos que te pertenezcan o para los que tengas autorización, y respeta los términos de servicio y la normativa de protección de datos aplicable.

Con cinco elementos lanzas la primera tarea:

  • Clave API de CaptchaAI — desde captchaai.com.
  • Valor gt — identificador estático, uno por sitio; lo extraes una vez y lo cacheas.
  • Valor challenge — dinámico, cambia en cada sesión; se vuelve a pedir en cada intento.
  • URL de la página — donde aparece el desafío.
  • Entorno — Python 3.7+ o Node.js 14+.

Paso 1: extraer los parámetros de GeeTest

Hay tres formas de conseguir el par gt/challenge, de la más rápida a la más automatizable. El tercer parámetro, api_server, es opcional y solo aparece cuando el sitio usa un servidor de verificación propio.

Método 1: la pestaña Network de DevTools

  1. Abre DevTools y ve a la pestaña Network.
  2. Filtra por register-slide, gettype.php o get.php.
  3. Dispara el CAPTCHA en la página y localiza la solicitud de inicialización.
  4. En la respuesta verás gt, challenge y, a veces, api_server.
{
  "success": 1,
  "gt": "019924a82c70bb123aae90d483087f94",
  "challenge": "12345678abc90def12345678abc90def",
  "new_captcha": true
}

Método 2: leer el código fuente de la página

Cuando el sitio inicializa el widget en línea, los valores viven en un <script> junto a la llamada initGeetest. Este fragmento en la consola te los saca.

// Search page source for initGeetest or gt value
document.querySelectorAll('script').forEach(s => {
  if (s.textContent.includes('initGeetest')) {
    console.log(s.textContent);
  }
});

Método 3: el endpoint de registro del propio sitio

La vía preferible si vas a automatizar. Muchos sitios piden los parámetros a su propia API antes de pintar el widget: replica esa llamada y tendrás un challenge fresco en cada iteración.

# The site's registration endpoint
params_response = requests.get("https://example.com/api/captcha/register")
data = params_response.json()
gt = data["gt"]
challenge = data["challenge"]

Paso 2: enviar la tarea a CaptchaAI

La tarea va a in.php con method=geetest. Añade api_server solo si el sitio especifica uno. La respuesta con status: 1 trae el identificador de la tarea en el campo request.

Python

import requests
import time

API_KEY = "YOUR_API_KEY"

response = requests.get("https://ocr.captchaai.com/in.php", params={
    "key": API_KEY,
    "method": "geetest",
    "gt": "019924a82c70bb123aae90d483087f94",
    "challenge": "12345678abc90def12345678abc90def",
    "api_server": "api.geetest.com",  # Optional, use if site specifies
    "pageurl": "https://staging.example.com/qa-login",
    "json": 1
})

data = response.json()
if data.get("status") != 1:
    raise Exception(f"Submit error: {data.get('request')}")

task_id = data["request"]
print(f"Task submitted: {task_id}")

Node.js

La misma llamada con axios, envuelta en una función reutilizable.

const axios = require('axios');

const API_KEY = 'YOUR_API_KEY';

async function submitGeeTest(gt, challenge, pageurl) {
  const { data } = await axios.get('https://ocr.captchaai.com/in.php', {
    params: {
      key: API_KEY,
      method: 'geetest',
      gt,
      challenge,
      api_server: 'api.geetest.com',
      pageurl,
      json: 1
    }
  });

  if (data.status !== 1) throw new Error(`Submit error: ${data.request}`);
  return data.request;
}

Paso 3: consultar el resultado

Sondeas res.php hasta que la tarea termine. Mientras el solver trabaja, la API responde CAPCHA_NOT_READY (así, con esa grafía); cualquier otro valor es un error real y conviene abortar en lugar de seguir consultando. Un intervalo de 5 segundos y 30 intentos cubren de sobra el margen habitual.

Python

def get_geetest_solution(task_id):
    for attempt in range(30):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY,
            "action": "get",
            "id": task_id,
            "json": 1
        }).json()

        if result.get("status") == 1:
            return result["request"]
        if result.get("request") != "CAPCHA_NOT_READY":
            raise Exception(f"Error: {result.get('request')}")

    raise Exception("Timeout")

solution = get_geetest_solution(task_id)
# solution = {
#   "geetest_challenge": "12345678abc90def12345678abc90def1a",
#   "geetest_validate": "abcdef1234567890abcdef1234567890",
#   "geetest_seccode": "abcdef1234567890abcdef1234567890|jordan"
# }

Node.js

Mismo bucle, con setTimeout envuelto en una promesa para no bloquear el hilo.

async function getGeeTestSolution(taskId) {
  for (let i = 0; i < 30; i++) {
    await new Promise(r => setTimeout(r, 5000));
    const { data } = await axios.get('https://ocr.captchaai.com/res.php', {
      params: { key: API_KEY, action: 'get', id: taskId, json: 1 }
    });
    if (data.status === 1) return data.request;
    if (data.request !== 'CAPCHA_NOT_READY') throw new Error(data.request);
  }
  throw new Error('Timeout');
}

Paso 4: enviar la solución al sitio de destino

Los tres valores viajan junto al resto del formulario, con los nombres de campo que espera el backend.

# Submit the GeeTest solution with the form data
verify_response = requests.post("https://example.com/api/login", data={
    "username": "[email protected]",
    "password": "password123",
    "geetest_challenge": solution["geetest_challenge"],
    "geetest_validate": solution["geetest_validate"],
    "geetest_seccode": solution["geetest_seccode"]
})

print(f"Login status: {verify_response.status_code}")

Si el sitio rechaza la solución, casi siempre es porque el challenge que resolviste ya no es el que el servidor tiene registrado para tu sesión.


Errores frecuentes y cómo resolverlos

Error Causa Solución
ERROR_BAD_PARAMETERS Falta gt o challenge Ambos son obligatorios: extráelos de la página
ERROR_CAPTCHA_UNSOLVABLE El challenge caducó o no es válido Pide uno nuevo al sitio y reintenta
El sitio rechaza la solución challenge obsoleto Es de un solo uso: consigue uno nuevo en cada intento
geetest_validate llega vacío La resolución no se completó Reintenta con un challenge recién obtenido

Hay un quinto caso que no lanza error pero desperdicia threads: reutilizar el mismo challenge en un bucle de reintentos. Si tu reintento no vuelve al paso 1, envías un valor muerto una y otra vez.


Cuántos threads necesitas

Un thread es un desafío en vuelo, así que el tramo lo marca tu concurrencia, no el volumen mensual:

  • BASIC ($15/mes, 5 threads) — suites nocturnas pequeñas y desarrollo local.
  • STANDARD ($30/mes, 15 threads) — un pipeline de CI con varios jobs en paralelo.
  • ADVANCE ($90/mes, 50 threads) — matrices de regresión que lanzan decenas de escenarios a la vez.

Ninguno de los tramos tiene tope de solves en el mes: pagas por concurrencia.


Ejemplo completo en Python

Los cuatro pasos encadenados: pedir parámetros, enviar, sondear y publicar la solución.

import requests
import time

API_KEY = "YOUR_API_KEY"
SITE_URL = "https://staging.example.com/qa-login"

# 1. Get GeeTest parameters from the site
params = requests.get("https://example.com/api/captcha/register").json()

# 2. Submit to CaptchaAI
submit = requests.get("https://ocr.captchaai.com/in.php", params={
    "key": API_KEY,
    "method": "geetest",
    "gt": params["gt"],
    "challenge": params["challenge"],
    "pageurl": SITE_URL,
    "json": 1
}).json()
task_id = submit["request"]

# 3. Poll for solution
for _ in range(30):
    time.sleep(5)
    result = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": API_KEY, "action": "get", "id": task_id, "json": 1
    }).json()
    if result.get("status") == 1:
        solution = result["request"]
        break

# 4. Submit to site
login = requests.post(SITE_URL, data={
    "username": "[email protected]",
    "password": "pass",
    "geetest_challenge": solution["geetest_challenge"],
    "geetest_validate": solution["geetest_validate"],
    "geetest_seccode": solution["geetest_seccode"]
})
print(f"Result: {login.status_code}")

¿Prefieres un proyecto con entorno, sondeo, reintentos y manejo de errores ya montado?

Ver el ejemplo ejecutable completo en GitHub →


Preguntas frecuentes

¿Cuántos threads necesito para una suite de pruebas nocturna?

Depende de la concurrencia, no del volumen. Cuenta cuántos escenarios lanzas a la vez: cinco en paralelo caben en BASIC ($15/mes, 5 threads); cincuenta piden ADVANCE ($90/mes, 50 threads).

¿Por qué mi integración funciona en local y falla en el pipeline?

Casi siempre por el challenge. En local lo copias fresco de DevTools; en el pipeline, si lo cacheaste junto al gt o lo reutilizas entre reintentos, llega muerto al solver. Cada intento debe volver al paso 1.

¿Puedo resolver GeeTest v3 sin abrir un navegador?

Sí, y es lo recomendable. Todo el flujo son solicitudes HTTP: pides los parámetros, envías la tarea y publicas los tres campos. Solo necesitas un navegador headless si el sitio no expone su endpoint de registro.

¿Sirve esta misma guía para GeeTest v4?

No. GeeTest v4 usa otro protocolo y aún no está disponible en CaptchaAI: figura como próximamente. Verifica en el código de la página qué versión se carga antes de reutilizar esta integración.


Guías relacionadas

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