Tutoriales de API

Parámetros del CAPTCHA deslizante de GeeTest y su API

Para resolver un CAPTCHA deslizante de GeeTest v3 con la API de CaptchaAI solo necesitas tres datos del sitio: gt, challenge y pageurl. Saber cuáles son es lo fácil; lo que hace fallar a casi todos es que el challenge caduca en segundos. Esta guía recorre el flujo completo con código en Python. Es un patrón habitual en el login de portales de trámites de la región.


El flujo de resolución en tres pasos

Antes de entrar en el código, este es el recorrido de principio a fin:

  1. Extraes gt y un challenge fresco de la página objetivo.
  2. Envías ambos valores a la API de CaptchaAI y sondeas hasta recibir la solución.
  3. Devuelves la terna challenge + validate + seccode al endpoint de validación del sitio.

Cada paso depende del anterior y el reloj empieza a correr en cuanto pides el challenge, así que conviene tener los tres tramos encadenados antes de lanzar el primero. Verás este mismo patrón en portales de cita previa, trámites de la administración pública y paneles de proveedores que protegen el acceso con GeeTest v3.


Los cuatro parámetros de GeeTest v3

Parámetro ¿Obligatorio? Qué es
gt ID de cuenta de GeeTest (hexadecimal de 32 caracteres). Aparece en el HTML o en la respuesta de la API.
challenge Cadena de desafío propia de cada sesión. Tiene que ir fresca en cada resolución.
pageurl URL completa de la página donde se muestra el CAPTCHA.
api_server No Subdominio del servidor de GeeTest cuando el sitio usa uno personalizado.

Paso 1: extraer gt y challenge de la página

El gt suele estar en el HTML; el challenge llega de una llamada register-slide. Este extractor prueba ambas vías:

# extract_geetest_params.py
import requests
import re
import json


def extract_geetest_v3(page_url, session=None):
    """Extract GeeTest v3 gt and challenge from a page."""
    if session is None:
        session = requests.Session()
        session.headers["User-Agent"] = (
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 Chrome/125.0.0.0 Safari/537.36"
        )

    resp = session.get(page_url, timeout=15)
    html = resp.text

    # Method 1: Extract gt from HTML
    gt_match = re.search(r'gt["\']?\s*[:=]\s*["\']([a-f0-9]{32})', html)
    gt = gt_match.group(1) if gt_match else None

    # Method 2: Find API endpoint that returns challenge
    api_match = re.search(r'(https?://[^"\']+register-slide[^"\']*)', html)

    challenge = None
    if api_match:
        api_url = api_match.group(1)
        api_resp = session.get(api_url, timeout=10)
        try:
            data = api_resp.json()
            challenge = data.get("challenge")
            gt = gt or data.get("gt")
        except json.JSONDecodeError:
            pass

    if not challenge:
        # Try embedded challenge
        ch_match = re.search(r'challenge["\']?\s*[:=]\s*["\']([a-f0-9]+)', html)
        challenge = ch_match.group(1) if ch_match else None

    return {"gt": gt, "challenge": challenge, "pageurl": page_url}


# Usage
params = extract_geetest_v3("https://staging.example.com/qa-login")
print(f"gt: {params['gt']}")
print(f"challenge: {params['challenge']}")

Paso 2: enviar el desafío a CaptchaAI

Envías la tarea a in.php y consultas el resultado en res.php:

# solve_geetest.py
import requests
import time
import os


def solve_geetest(gt, challenge, pageurl, api_server=None):
    """Solve GeeTest v3 slide CAPTCHA via CaptchaAI."""
    api_key = os.environ["CAPTCHAAI_API_KEY"]

    payload = {
        "key": api_key,
        "method": "geetest",
        "gt": gt,
        "challenge": challenge,
        "pageurl": pageurl,
        "json": 1,
    }

    if api_server:
        payload["api_server"] = api_server

    # Submit
    resp = requests.post(
        "https://ocr.captchaai.com/in.php",
        data=payload,
        timeout=30,
    )
    result = resp.json()

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

    task_id = result["request"]

    # Poll — GeeTest typically solves in 10-20 seconds
    time.sleep(10)
    for _ in range(30):
        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:
            return data["request"]  # Returns challenge, validate, seccode
        if data["request"] != "CAPCHA_NOT_READY":
            raise RuntimeError(data["request"])
        time.sleep(5)

    raise TimeoutError("GeeTest solve timeout")

Un GeeTest v3 se resuelve en 10-20 segundos: por eso se espera 10 s antes del primer sondeo.


Paso 3: devolver la solución al sitio

CaptchaAI devuelve tres valores —challenge, validate y seccode— que reenvías al endpoint de validación del sitio. Si falta cualquiera, se rechaza:

# submit_solution.py
import json


def submit_geetest_solution(session, validation_url, solution, original_challenge):
    """Submit GeeTest solution to the target site."""
    # Parse solution if string
    if isinstance(solution, str):
        solution = json.loads(solution)

    payload = {
        "geetest_challenge": solution.get("challenge", original_challenge),
        "geetest_validate": solution.get("validate", ""),
        "geetest_seccode": solution.get("seccode", ""),
    }

    resp = session.post(validation_url, data=payload, timeout=30)
    return resp


# Complete flow
def full_geetest_flow(page_url, validation_url):
    import requests
    from extract_geetest_params import extract_geetest_v3

    session = requests.Session()
    session.headers["User-Agent"] = (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 Chrome/125.0.0.0 Safari/537.36"
    )

    # Step 1: Extract parameters
    params = extract_geetest_v3(page_url, session)
    print(f"gt: {params['gt']}, challenge: {params['challenge'][:16]}...")

    # Step 2: Solve
    solution = solve_geetest(
        params["gt"], params["challenge"], params["pageurl"],
    )
    print("Solved!")

    # Step 3: Submit
    resp = submit_geetest_solution(
        session, validation_url, solution, params["challenge"],
    )
    print(f"Validation response: {resp.status_code}")
    return resp

Mantén la misma sesión de principio a fin

El sitio ata el challenge a las cookies con las que lo pediste. Si lo extraes con una session y envías la solución con otra, el servidor ve un cliente distinto y rechaza la validación aunque los tres campos vayan correctos. Por eso el mismo objeto requests.Session() recorre las tres fases —extracción, resolución y envío— sin recrearse por el camino. Es el segundo motivo de fallos más habitual, justo detrás del challenge caducado.


Por qué el challenge caduca en segundos

El challenge expira muy rápido: si lo envías un minuto después, ya no sirve. Pídelo justo antes de resolver:

# fresh_challenge.py
import time


def get_fresh_challenge(session, register_url):
    """Always fetch a fresh challenge before solving."""
    resp = session.get(register_url, timeout=10)
    data = resp.json()

    challenge = data.get("challenge")
    if not challenge:
        raise ValueError("No challenge returned")

    return challenge


def solve_with_fresh_challenge(session, gt, register_url, pageurl):
    """Ensure challenge is fresh before submitting to CaptchaAI."""
    challenge = get_fresh_challenge(session, register_url)

    # Submit immediately — don't let it expire
    solution = solve_geetest(gt, challenge, pageurl)
    return solution

Regla clave: un desafío caducado siempre falla, por correcto que sea el resto del código.


Sitios con servidor GeeTest personalizado

Algunos sitios enrutan GeeTest por un subdominio propio. Indica entonces el parámetro api_server; si lo omites, la resolución falla:

# The api_server parameter specifies a custom GeeTest backend
# Default: api.geetest.com
# Custom examples: api-na.geetest.com, api.geetest.com/ajax-custom

solution = solve_geetest(
    gt="abc123...",
    challenge="def456...",
    pageurl="https://staging.example.com/qa-login",
    api_server="api-na.geetest.com",  # North America endpoint
)

Lista de comprobación antes de enviar

  • El gt tiene 32 caracteres hexadecimales.
  • El challenge procede de una llamada register-slide reciente, no del HTML cacheado.
  • Usas la misma session en las tres fases.
  • Incluyes api_server solo si el sitio enruta GeeTest por un subdominio propio.
  • Reenvías los tres campos que devuelve CaptchaAI: challenge, validate y seccode.

Errores frecuentes y cómo resolverlos

Problema Causa Solución
ERROR_CAPTCHA_UNSOLVABLE El challenge ya caducó Pide uno nuevo justo antes de enviarlo
validate llega vacío El sitio no es GeeTest v3 Puede ser GeeTest v4, un protocolo distinto (próximamente en CaptchaAI)
El sitio rechaza la solución Falta seccode Envía los tres campos: challenge, validate y seccode
No aparece el parámetro gt Se carga por JavaScript Usa Selenium o revisa las respuestas XHR

Preguntas frecuentes

¿GeeTest v3 y GeeTest v4 se resuelven igual?

No, son protocolos distintos. CaptchaAI resuelve GeeTest v3 con el flujo gt + challenge de esta guía; GeeTest v4 aún no está disponible (próximamente).

¿Necesito un navegador headless para extraer los parámetros?

Depende. Si el gt y register-slide están en el HTML o en respuestas XHR, basta con requests; si el widget se monta por JavaScript, usa Selenium en modo headless.

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

Se factura por thread concurrente, con resoluciones ilimitadas por thread. BASIC ($15/mes, 5 threads) cubre proyectos pequeños; para miles de GeeTest por hora, sube a ADVANCE ($90/mes, 50 threads).

¿Por qué el sitio rechaza una solución correcta?

Casi siempre porque falta uno de los tres campos (challenge/validate/seccode), porque el challenge ya había caducado o porque cambiaste de session entre la extracción y el envío.

¿Puedo reutilizar un challenge para varias resoluciones?

No. Cada challenge vale para una sola resolución y expira en segundos. Pide uno nuevo con register-slide justo antes de cada intento; reciclarlo provoca un ERROR_CAPTCHA_UNSOLVABLE.


Guías relacionadas


Domina los parámetros de GeeTest: empieza con CaptchaAI.

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