Explicaciones Técnicas

Explicación de los sistemas CAPTCHA invisibles: cómo funcionan

Tu script rellena el formulario, pulsa enviar y no pasa nada: ni casilla, ni cuadrícula de imágenes, ni error claro; la solicitud simplemente se rechaza. Casi siempre el culpable es un CAPTCHA invisible: un sistema que verifica al usuario en segundo plano, sin mostrar ningún desafío. En lugar de pedirte que resuelvas algo, analiza mediante JavaScript el comportamiento del navegador, las señales del dispositivo y la reputación de la red, y decide en silencio si te deja pasar. Si un formulario se envía limpio para unos visitantes y bloquea a otros sin CAPTCHA a la vista, estás ante uno de estos sistemas.

Es un patrón que aparece constantemente en escenarios muy comunes en la región:

  • Portales de citas de visado BLS, con enorme flujo de solicitantes latinoamericanos.
  • Trámites de cita previa de administraciones públicas (SAT en México, AFIP en Argentina, sedes electrónicas en España).
  • Monitoreo de precios en marketplaces del estilo de MercadoLibre o Amazon.es.

En todos ellos el flujo funciona en el navegador, pero tu automatización recibe un 403 sin explicación. Trabaja siempre sobre flujos autorizados y respeta los términos de servicio y la normativa de protección de datos.


Cómo verifica un CAPTCHA invisible sin mostrar nada

Todos los CAPTCHA invisibles siguen el mismo patrón central:

Page loads → CAPTCHA JavaScript injected
    ↓
Script runs in background, collecting signals:
  ├─ Browser fingerprint (canvas, WebGL, fonts, plugins)
  ├─ Behavioral data (mouse, keyboard, scroll, timing)
  ├─ Network data (IP reputation, TLS fingerprint)
  ├─ Device data (screen, GPU, CPU cores, memory)
  └─ History data (cookies, previous CAPTCHA completions)
    ↓
Signals sent to CAPTCHA provider's risk engine
    ↓
Risk score computed
    ↓
Response varies by score:
  ├─ Low risk  → Token issued silently (user sees nothing)
  ├─ Medium    → Light challenge shown (checkbox, simple click)
  └─ High risk → Full challenge (image grid) or block

Las señales que más pesan se agrupan en cuatro familias:

  • Huella del navegador: canvas, WebGL, fuentes y plugins.
  • Comportamiento: movimiento del ratón, tecleo, scroll y tiempos.
  • Red: reputación de la IP y huella TLS.
  • Historial: cookies y resoluciones previas de CAPTCHA.

La diferencia esencial con un CAPTCHA visible: aquí la verificación corre de forma continua desde que carga la página, no solo cuando el usuario hace clic en una casilla. Para cuando envías el formulario, el proveedor ya tiene un veredicto sobre ti.


Los cinco sistemas invisibles que vas a encontrar

1. reCAPTCHA v3

  • Proveedor: Google
  • Lanzamiento: 2018
  • Cuota de mercado: la más alta entre los CAPTCHA invisibles

reCAPTCHA v3 devuelve una puntuación decimal entre 0,0 y 1,0 para cada acción de la página. Es el propio sitio quien decide qué hacer con esa puntuación.

Cómo recopila señales:

<!-- reCAPTCHA v3 integration -->
<script src="https://www.google.com/recaptcha/api.js?render=SITE_KEY"></script>
<script>
  grecaptcha.ready(function() {
    grecaptcha.execute('SITE_KEY', { action: 'login' }).then(function(token) {
      // Token sent to server for verification
      document.getElementById('captcha-token').value = token;
    });
  });
</script>

La llamada execute() dispara la recopilación de señales bajo demanda. En realidad, reCAPTCHA lleva recogiendo datos desde que se cargó el script.

Cómo interpretar la puntuación:

Puntuación Significado Acción típica
0,9 muy probablemente humano Permitir
0,7 probablemente humano Permitir con seguimiento
0,5 incierto Exigir verificación adicional
0,3 probablemente bot Bloquear o mostrar reCAPTCHA v2
0,1 muy probablemente bot Bloquear

Cómo detectarlo en el HTML:

import requests
import re

def detect_recaptcha_v3(url):
    html = requests.get(url, timeout=10).text
    indicators = {
        "recaptcha_v3": False,
        "site_key": None,
        "actions": [],
    }

    # Check for v3 script
    if "recaptcha/api.js?render=" in html:
        indicators["recaptcha_v3"] = True
        match = re.search(r"render=([A-Za-z0-9_-]+)", html)
        if match:
            indicators["site_key"] = match.group(1)

    # Find action names
    actions = re.findall(r"action:\s*['\"](\w+)['\"]", html)
    indicators["actions"] = actions

    return indicators

print(detect_recaptcha_v3("https://staging.example.com/qa-login"))

2. Cloudflare Turnstile

  • Proveedor: Cloudflare
  • Lanzamiento: 2022
  • Cuota de mercado: el de más rápido crecimiento

Turnstile encadena varias etapas de verificación sin necesidad de recopilar datos personales del usuario.

Etapas de verificación:

  1. Comprobación de Private Access Token: si el navegador admite Apple PAT, la verificación se completa al instante.
  2. Desafío no interactivo: el navegador resuelve un ligero rompecabezas criptográfico de prueba de trabajo (proof-of-work).
  3. Desafío gestionado: Cloudflare elige entre interactivo y no interactivo según las señales de riesgo de su red (más del 20% del tráfico de internet pasa por Cloudflare).
  4. Desafío interactivo: solo aparece cuando la puntuación de riesgo es alta; muestra un widget estilo casilla.

Integración:

<!-- Turnstile widget -->
<div class="cf-turnstile" data-sitekey="0x4AAAAAAAC3DHQhMMQ_Rxrg"></div>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>

Cómo detectarlo:

def detect_turnstile(url):
    html = requests.get(url, timeout=10).text
    indicators = {
        "turnstile": False,
        "site_key": None,
    }

    if "challenges.cloudflare.com/turnstile" in html or "cf-turnstile" in html:
        indicators["turnstile"] = True
        match = re.search(r'data-sitekey="([^"]+)"', html)
        if match:
            indicators["site_key"] = match.group(1)

    return indicators

3. reCAPTCHA v2 Invisible

  • Proveedor: Google
  • Lanzamiento: 2017

Es reCAPTCHA v2 disparado por código al enviar el formulario. No hay casilla visible: el usuario pulsa el botón de enviar y reCAPTCHA se ejecuta en segundo plano. Si la puntuación de riesgo es lo bastante alta, aparece un desafío de imágenes como ventana emergente.

Integración:

<button class="g-recaptcha"
        data-sitekey="SITE_KEY"
        data-callback="onSubmit"
        data-size="invisible">
    Submit
</button>

En qué se diferencia de la v3: la v2 Invisible todavía recurre a un desafío visible para los visitantes de riesgo. La v3 nunca muestra desafío: es el sitio quien debe decidir según la puntuación.

4. hCaptcha Passive (Enterprise)

  • Proveedor: Intuition Machines
  • Disponibilidad: solo nivel Enterprise

El modo pasivo de hCaptcha Enterprise evalúa al usuario sin ningún widget visible. Usa las mismas señales de comportamiento que el hCaptcha estándar, pero solo presenta un desafío visual a los visitantes más sospechosos.

5. Apple Private Access Tokens

  • Proveedor: Apple
  • Disponibilidad: iOS 16+, macOS Ventura+

Esto no es un proveedor de CAPTCHA, sino un mecanismo para omitirlos. Los dispositivos Apple demuestran su legitimidad mediante certificación de hardware, lo que permite a los sitios saltarse el CAPTCHA por completo.

Device attestation:

  1. Website requests a challenge from a token issuer
  2. Device proves it is genuine without revealing identity
  3. Token issuer generates a blind-signed token
  4. Website verifies token → CAPTCHA skipped

Compatible con Cloudflare, Fastly y otros proveedores de CDN.


Comparativa rápida: cuál devuelve puntuación y cuál bloquea

Antes de escribir una sola línea de integración conviene tener claro cómo se comporta cada sistema. Esta tabla resume lo que importa para automatizar.

Característica reCAPTCHA v3 Turnstile reCAPTCHA v2 Invisible hCaptcha Passive
Reto visible Nunca Rara vez A veces (de respaldo) Rara vez
Privacidad Recopila datos de navegación Sin datos personales Recopila datos de navegación Recopila datos de interacción
Coste Gratis (1M/mes) Gratis (ilimitado) Gratis (1M/mes) Solo Enterprise
Devuelve puntuación Sí (0,0-1,0) No (pasa/falla) No (pasa/falla) Sí (Enterprise)
Reto visible de respaldo Lo decide el sitio Gestionado por Cloudflare Popup de cuadrícula de imágenes Popup de cuadrícula de imágenes
Compatibilidad con CaptchaAI Sí (token v3) Sí (alta tasa de éxito) Sí (token) ❌ No compatible (por ahora)

Cómo resolver CAPTCHA invisibles con CaptchaAI

Aunque no muestren nada, estos sistemas siguen generando un token que hay que enviar junto con el formulario. Un solver por API produce ese token fuera del navegador en dos pasos:

  • Envías el sitekey y la URL de la página al endpoint in.php y obtienes un identificador de tarea.
  • Consultas el resultado en res.php hasta recibir el g-recaptcha-response o cf-turnstile-response.

reCAPTCHA v3

import requests
import time

API_KEY = "YOUR_API_KEY"

submit = requests.post("https://ocr.captchaai.com/in.php", data={
    "key": API_KEY,
    "method": "userrecaptcha",
    "googlekey": "6LdKlZEpAAAAAAOQjzC2v_d36tWxCl6dWsozdSy9",
    "pageurl": "https://staging.example.com/qa-login",
    "version": "v3",
    "action": "login",
    "json": 1,
})

task_id = submit.json()["request"]

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,
        "json": 1,
    }).json()

    if result.get("status") == 1:
        token = result["request"]
        print(f"reCAPTCHA v3 token received (score ≥ 0.9)")
        break

Política de puntuación para v3

Como la v3 no bloquea por sí sola, tu backend decide el umbral. Un patrón sensato: aceptar por encima de 0,7, exigir un paso adicional (por ejemplo reCAPTCHA v2) en la franja intermedia y bloquear por debajo.

def decide_v3_action(score: float) -> str:
    if score >= 0.9:
        return "submit"
    if score >= 0.5:
        return "step_up_to_v2"
    return "block_or_review"

Cloudflare Turnstile

submit = requests.post("https://ocr.captchaai.com/in.php", data={
    "key": API_KEY,
    "method": "turnstile",
    "sitekey": "0x4AAAAAAAC3DHQhMMQ_Rxrg",
    "pageurl": "https://example.com/signup",
    "json": 1,
})

task_id = submit.json()["request"]

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,
        "json": 1,
    }).json()

    if result.get("status") == 1:
        turnstile_token = result["request"]
        print(f"Turnstile token received")
        break

Node.js: reCAPTCHA v3

const axios = require("axios");

async function solveRecaptchaV3(siteKey, pageUrl, action) {
    const API_KEY = "YOUR_API_KEY";

    // Submit
    const { data: submit } = await axios.post(
        "https://ocr.captchaai.com/in.php",
        new URLSearchParams({
            key: API_KEY,
            method: "userrecaptcha",
            googlekey: siteKey,
            pageurl: pageUrl,
            version: "v3",
            action: action,
            json: 1,
        })
    );

    const taskId = submit.request;

    // Poll
    for (let i = 0; i < 60; i++) {
        await new Promise((r) => setTimeout(r, 5000));
        const { data: result } = await axios.get(
            "https://ocr.captchaai.com/res.php",
            {
                params: {
                    key: API_KEY,
                    action: "get",
                    id: taskId,
                    json: 1,
                },
            }
        );

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

    throw new Error("Solve timeout");
}

solveRecaptchaV3("SITE_KEY", "https://staging.example.com/qa-login", "login")
    .then((token) => console.log("Token:", token.substring(0, 50) + "..."));

Cómo detectar cuál protege una página

Como el widget no tiene presencia visible, la única forma fiable de saber a qué te enfrentas es mirar el HTML y las peticiones de red. Este verificador agrupa las firmas de los cuatro sistemas más comunes en una sola pasada:

import requests
import re

def detect_invisible_captcha(url):
    """Detect all invisible CAPTCHA types on a page."""
    html = requests.get(url, timeout=10).text

    results = []

    # reCAPTCHA v3
    if "recaptcha/api.js?render=" in html:
        key = re.search(r"render=([A-Za-z0-9_-]+)", html)
        results.append({
            "type": "reCAPTCHA v3",
            "site_key": key.group(1) if key else "unknown",
            "solver_method": "userrecaptcha (version=v3)",
        })

    # reCAPTCHA v2 invisible
    if 'data-size="invisible"' in html and "g-recaptcha" in html:
        key = re.search(r'data-sitekey="([^"]+)"', html)
        results.append({
            "type": "reCAPTCHA v2 Invisible",
            "site_key": key.group(1) if key else "unknown",
            "solver_method": "userrecaptcha (invisible=1)",
        })

    # Cloudflare Turnstile
    if "challenges.cloudflare.com/turnstile" in html:
        key = re.search(r'data-sitekey="([^"]+)"', html)
        results.append({
            "type": "Cloudflare Turnstile",
            "site_key": key.group(1) if key else "unknown",
            "solver_method": "turnstile",
        })

    # hCaptcha (passive mode detected by script without visible widget)
    if "hcaptcha.com/1/api.js" in html and 'data-size="invisible"' in html:
        results.append({
            "type": "hCaptcha Passive",
            "solver_method": "hcaptcha",
        })

    if not results:
        results.append({"type": "none detected"})

    return results

for item in detect_invisible_captcha("https://example.com"):
    print(item)

Con el método ya identificado —userrecaptcha para las variantes de reCAPTCHA, turnstile para Cloudflare Turnstile— puedes encadenar esta detección con los ejemplos anteriores y pedir el token adecuado de forma automática.


Preguntas frecuentes

¿Por qué mi automatización se bloquea sin que aparezca ningún CAPTCHA?

Porque el token que espera el servidor no viaja en el envío. Si tu flujo no adjunta un g-recaptcha-response o cf-turnstile-response válido, el backend rechaza la solicitud en silencio o devuelve un error genérico. No verás ningún desafío: la petición falla sin más porque falta el token o no es válido.

¿CaptchaAI puede resolver reCAPTCHA v3 y Turnstile invisibles?

Sí. CaptchaAI genera el token de reCAPTCHA v3 con el método userrecaptcha y version=v3, y el de Cloudflare Turnstile con el método turnstile, con una alta tasa de éxito. hCaptcha (incluido su modo pasivo) no está entre los tipos compatibles por ahora.

¿Qué umbral de puntuación conviene exigir en reCAPTCHA v3?

Depende del riesgo de la acción:

  • Login o pago: exige 0,7 o superior; deja fuera a la mayoría de los bots.
  • Acciones de bajo impacto: puedes ser más permisivo y bajar el umbral.
  • Franja intermedia: combina la v3 con la v2 como respaldo para no penalizar a usuarios legítimos.

Este esquema suele dar el mejor equilibrio entre seguridad y falsos positivos.

¿Cuánto cuesta resolver muchos CAPTCHA invisibles al mes?

CaptchaAI cobra por thread concurrente, no por resolución, con resoluciones ilimitadas en cada plan. El plan BASIC cuesta $15/mes con 5 threads, y escalas a STANDARD ($30/mes, 15 threads) o superiores según tu volumen. Es un coste mensual predecible en USD, útil si facturas en una moneda local volátil.


En resumen

Los CAPTCHA invisibles verifican al usuario en segundo plano, sin ninguna interacción visible. reCAPTCHA v3 y Cloudflare Turnstile dominan el panorama, y Turnstile es el que más crece por su precio gratuito y su enfoque en la privacidad. Para automatizar, lo importante es que siguen exigiendo un token válido: primero detecta cuál protege la página, luego genera el token con CaptchaAI —reCAPTCHA v3 con userrecaptcha y version=v3, Turnstile con el método turnstile— y adjúntalo al formulario.

Artículos relacionados

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