Casos de Uso

Scripts de automatización CAPTCHA con CaptchaAI

¿Quieres resolver un CAPTCHA desde un script, sin integrar una librería pesada ni levantar un navegador? Estos seis scripts hacen justo eso: hablan con la API de CaptchaAI por HTTP y devuelven el token. Todos siguen el mismo patrón: envían la tarea a in.php y sondean res.php. Cambia YOUR_API_KEY por tu clave y ejecuta.

El patrón común: enviar y sondear

Los seis scripts comparten la misma mecánica en dos pasos, así que entenderla una vez te sirve para todos. Primero envían la tarea con una petición GET a in.php, indicando el method (el tipo de CAPTCHA) y sus parámetros; la API responde con OK| seguido del ID de la tarea. Después consultan res.php cada 5 segundos con ese ID hasta que la respuesta deja de ser CAPCHA_NOT_READY y llega OK|token. Ese token es lo que tu automatización inyecta después en el formulario objetivo. Entre un script y otro solo cambian el method y los parámetros de entrada; el ciclo de sondeo es idéntico.

Script 1: resolver reCAPTCHA v2

El caso más habitual: le pasas el sitekey (googlekey) y la URL de la página, y el script imprime el token.

#!/usr/bin/env python3
"""Solve reCAPTCHA v2 and print the token."""
import requests
import time
import sys

API_KEY = "YOUR_API_KEY"

def solve_recaptcha_v2(site_key, page_url):
    resp = requests.get("https://ocr.captchaai.com/in.php", params={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": site_key,
        "pageurl": page_url
    })
    if not resp.text.startswith("OK|"):
        print(f"Error: {resp.text}", file=sys.stderr)
        sys.exit(1)

    task_id = resp.text.split("|")[1]
    print(f"Task ID: {task_id}")

    for _ in range(60):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": task_id
        })
        if result.text == "CAPCHA_NOT_READY":
            print(".", end="", flush=True)
            continue
        if result.text.startswith("OK|"):
            print()
            return result.text.split("|")[1]
        print(f"\nError: {result.text}", file=sys.stderr)
        sys.exit(1)

    print("\nTimeout", file=sys.stderr)
    sys.exit(1)

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print(f"Usage: {sys.argv[0]} <site_key> <page_url>")
        sys.exit(1)
    token = solve_recaptcha_v2(sys.argv[1], sys.argv[2])
    print(token)

Uso:

python solve_recaptcha.py "6Le-wvkS..." "https://example.com/form"

Este primer script es el más completo de los seis: comprueba que in.php haya respondido OK| antes de seguir, imprime el ID de la tarea y muestra un punto por cada intento de sondeo para que veas el progreso en la terminal. Si algo falla —clave inválida, sitekey incorrecto o tiempo agotado tras 60 intentos— sale con un código distinto de cero, lo que te permite encadenarlo dentro de un pipeline y detectar el error sin ambigüedad.

Script 2: resolver Cloudflare Turnstile

Mismo esquema con method=turnstile. Turnstile aparece en cada vez más formularios de registro y checkout; este script lo cubre igual.

Fíjate en el único cambio real respecto al anterior: Turnstile identifica la página con el parámetro sitekey, no con googlekey. El resto —el bucle, los intervalos de 5 segundos, la comprobación de OK|— es idéntico. Turnstile suele resolverse rápido, así que rara vez agotarás los 60 intentos del bucle.

#!/usr/bin/env python3
"""Solve Cloudflare Turnstile and print the token."""
import requests
import time

API_KEY = "YOUR_API_KEY"

def solve_turnstile(site_key, page_url):
    resp = requests.get("https://ocr.captchaai.com/in.php", params={
        "key": API_KEY,
        "method": "turnstile",
        "sitekey": site_key,
        "pageurl": page_url
    })
    task_id = resp.text.split("|")[1]

    for _ in range(60):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": task_id
        })
        if result.text == "CAPCHA_NOT_READY": continue
        if result.text.startswith("OK|"): return result.text.split("|")[1]
        raise Exception(result.text)
    raise TimeoutError()

token = solve_turnstile("0x4AAAAA...", "https://example.com")
print(token)

Script 3: resolver CAPTCHA de imagen

Para los CAPTCHA de imagen clásicos (texto distorsionado, OCR). Acepta una ruta local o una URL y devuelve el texto reconocido.

Aquí no hay sitekey: en vez de identificar un widget, envías la propia imagen codificada en base64 con method=base64. El script detecta solo si le pasas una ruta de archivo local o una URL http y la carga en consecuencia. Es la opción para los CAPTCHA de texto que todavía sobreviven en portales heredados y en muchos trámites de administraciones públicas.

#!/usr/bin/env python3
"""Solve an image CAPTCHA from a file or URL."""
import requests
import base64
import time
import sys

API_KEY = "YOUR_API_KEY"

def solve_image(image_source):
    # Load image
    if image_source.startswith("http"):
        img_data = requests.get(image_source).content
    else:
        with open(image_source, "rb") as f:
            img_data = f.read()

    img_b64 = base64.b64encode(img_data).decode()

    resp = requests.get("https://ocr.captchaai.com/in.php", params={
        "key": API_KEY,
        "method": "base64",
        "body": img_b64
    })
    task_id = resp.text.split("|")[1]

    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
        })
        if result.text == "CAPCHA_NOT_READY": continue
        if result.text.startswith("OK|"): return result.text.split("|")[1]
        raise Exception(result.text)
    raise TimeoutError()

if __name__ == "__main__":
    text = solve_image(sys.argv[1])
    print(text)

Uso:

python solve_image.py captcha.png
python solve_image.py "https://example.com/captcha.jpg"

Script 4: resolución de CAPTCHA por lotes

Para resolver muchos CAPTCHA a la vez —un scraping de precios en un marketplace como MercadoLibre o Amazon.es— el límite es la concurrencia. Ajusta max_workers a los threads de tu plan: BASIC ($15/mes, 5 threads) admite max_workers=5; ENTERPRISE ($300/mes, 200 threads), mucho más.

El script usa ThreadPoolExecutor y devuelve una lista de diccionarios con el estado de cada tarea (ok o failed), de modo que un fallo aislado no tumba el lote entero: procesas los resultados buenos y reintentas solo los que fallaron. Subir max_workers por encima de los threads que tienes contratados no acelera nada; simplemente encola las solicitudes de más a la espera de un thread libre.

#!/usr/bin/env python3
"""Solve multiple CAPTCHAs concurrently."""
import requests
import time
from concurrent.futures import ThreadPoolExecutor, as_completed

API_KEY = "YOUR_API_KEY"

def solve_one(site_key, page_url):
    resp = requests.get("https://ocr.captchaai.com/in.php", params={
        "key": API_KEY, "method": "userrecaptcha",
        "googlekey": site_key, "pageurl": page_url
    })
    task_id = resp.text.split("|")[1]

    for _ in range(60):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": task_id
        })
        if result.text == "CAPCHA_NOT_READY": continue
        if result.text.startswith("OK|"): return result.text.split("|")[1]
        raise Exception(result.text)
    raise TimeoutError()

def solve_batch(tasks, max_workers=5):
    """
    tasks: list of (site_key, page_url) tuples
    Returns: list of tokens
    """
    results = []
    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        futures = {
            executor.submit(solve_one, sk, url): (sk, url)
            for sk, url in tasks
        }
        for future in as_completed(futures):
            sk, url = futures[future]
            try:
                token = future.result()
                results.append({"url": url, "token": token, "status": "ok"})
            except Exception as e:
                results.append({"url": url, "error": str(e), "status": "failed"})
    return results

# Example
tasks = [
    ("6Le-wvkS...", "https://example.com/page1"),
    ("6Le-wvkS...", "https://example.com/page2"),
    ("6Le-wvkS...", "https://example.com/page3"),
]
results = solve_batch(tasks)
for r in results:
    print(f"{r['url']}: {r['status']}")

Script 5: resolvedor universal en Node.js

Un resolvedor único para cualquier tipo: le pasas los parámetros (method, sitekey/googlekey, pageurl) y aplica el mismo ciclo. Útil si tu backend está en JavaScript.

La función recibe un objeto params y le añade tu clave automáticamente, así que el mismo solve() sirve para reCAPTCHA, Turnstile o imágenes según lo que le pases. Al exportarse con module.exports, lo importas como módulo dentro de un servicio Node.js más grande en lugar de ejecutarlo suelto desde la terminal.

#!/usr/bin/env node
// Solve any CAPTCHA type from the command line
const axios = require("axios");

const API_KEY = "YOUR_API_KEY";

async function solve(params) {
  params.key = API_KEY;
  const submit = await axios.get("https://ocr.captchaai.com/in.php", {
    params,
  });
  if (!submit.data.startsWith("OK|")) throw new Error(submit.data);
  const taskId = submit.data.split("|")[1];

  while (true) {
    await new Promise((r) => setTimeout(r, 5000));
    const result = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: API_KEY, action: "get", id: taskId },
    });
    if (result.data === "CAPCHA_NOT_READY") continue;
    if (result.data.startsWith("OK|")) return result.data.split("|")[1];
    throw new Error(result.data);
  }
}

// Usage examples:
// Solve reCAPTCHA v2
// solve({ method: "userrecaptcha", googlekey: "SITE_KEY", pageurl: "URL" })

// Solve Turnstile
// solve({ method: "turnstile", sitekey: "SITE_KEY", pageurl: "URL" })

module.exports = { solve };

Consultar el saldo de tu cuenta

Antes de un lote grande, comprueba el saldo: getbalance te lo devuelve en USD, la misma moneda en la que se factura tu plan. Consultarlo antes de lanzar miles de resoluciones te evita que un lote se quede a medias por quedarte sin fondos.

#!/usr/bin/env python3
"""Check CaptchaAI account balance."""
import requests

API_KEY = "YOUR_API_KEY"

resp = requests.get("https://ocr.captchaai.com/res.php", params={
    "key": API_KEY,
    "action": "getbalance"
})
print(f"Balance: ${resp.text}")

Llevar estos scripts a producción

Los ejemplos son deliberadamente cortos para entenderse de un vistazo, pero antes de ponerlos en producción conviene añadir tres cosas. Primero, registro (logging) en lugar de print, para auditar qué tarea falló y por qué. Segundo, reintentos con retroceso exponencial ante errores transitorios de red, en vez de abortar al primer fallo. Y tercero, leer la clave desde una variable de entorno en lugar de dejarla escrita en el código: nunca subas tu API key a un repositorio. Con esas tres piezas, cualquiera de los seis scripts pasa de prueba de concepto a componente fiable de tu pipeline. Y recuerda respetar los términos de servicio del sitio objetivo y la normativa de protección de datos aplicable cuando automatices flujos que no son tuyos.

Preguntas frecuentes

¿Por qué los scripts consultan el resultado en bucle en vez de esperar la respuesta directa?

Porque la resolución no es instantánea. in.php devuelve un ID y consultas res.php cada 5 segundos hasta recibir OK|token, sin bloquear la conexión mientras el servicio trabaja.

¿Cuántos CAPTCHA puedo resolver en paralelo?

Depende de tu plan. CaptchaAI factura por threads concurrentes —cada thread es un CAPTCHA en curso—, no por resolución. Ajusta max_workers a tus threads disponibles.

¿Cuánto cuesta ejecutar estos scripts?

CaptchaAI usa precios basados en threads con resoluciones ilimitadas por thread: desde BASIC ($15/mes, 5 threads) hasta ENTERPRISE ($300/mes, 200 threads). No hay coste por CAPTCHA individual ni recargo por tipo.

¿Qué diferencia hay entre googlekey y sitekey?

Son el mismo concepto —la clave pública del CAPTCHA en la web objetivo— con distinto nombre según el proveedor: reCAPTCHA v2 lo llama googlekey y Turnstile lo llama sitekey. Copia el valor del atributo data-sitekey del widget en el HTML de la página y pásalo al script.

¿Necesito un proxy para ejecutar estos scripts?

No para el uso general. La resolución ocurre del lado de CaptchaAI, así que tu script solo hace peticiones HTTP a la API. Un proxy solo entra en juego si la web objetivo exige que la interacción provenga de una IP concreta, algo poco frecuente con reCAPTCHA v2 y Turnstile estándar.

Guías relacionadas

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