Tutoriales de API

Explicación del parámetro reCAPTCHA Data-S

Si Google rechaza tus tokens en la página de "tráfico inusual", casi siempre falta el mismo atributo: data-s, un token de sesión que Google inserta en el widget de reCAPTCHA y que solo vale para esa carga de página. Si envías la tarea con sitekey y pageurl pero sin data-s, el token no coincide con la sesión que espera el servidor y la validación falla sin error útil.

Afecta a pocas páginas y detectarlo cuesta una línea de código. Veamos dónde aparece, cómo extraerlo y cómo pasarlo a la API de CaptchaAI.


¿Dónde aparece data-s y cuándo puedes ignorarlo?

En la mayoría de los sitios con reCAPTCHA, data-s no existe. Aparece casi en exclusiva en propiedades de Google:

Sitio ¿Aparece data-s? Notas
Búsqueda de Google (páginas /sorry, tráfico inusual) Siempre Sin él la resolución no valida
YouTube A veces En ciertos flujos de verificación
Google Play A veces Verificación en fichas de aplicaciones
Formularios de Google Rara vez Implementaciones puntuales
Sitios de terceros con reCAPTCHA Casi nunca Las integraciones estándar no lo usan

Un caso típico: una agencia en Ciudad de México monitorea posiciones de búsqueda y Google empieza a servirle la página /sorry. Como el sitekey no cambia, el equipo lo deja fijo y recibe tokens que Google descarta: nadie leía data-s.

  • Si tu objetivo es una propiedad de Google, da por hecho que data-s puede estar y compruébalo en cada carga.
  • Si es un formulario de terceros (una tienda WordPress, por ejemplo), casi seguro no lo lleva.
  • Trabaja sobre sitios que te corresponden y respeta la normativa de protección de datos aplicable (GDPR y LOPDGDD en España, LFPDPPP en México).

Qué es data-s y dónde vive dentro del widget

data-s lo genera el servidor y viaja en el HTML del widget. Ata el desafío a una sesión concreta: un token resuelto en una no sirve en otra.

Cómo se ve en el HTML

<!-- reCAPTCHA widget with data-s parameter -->
<div class="g-recaptcha"
     data-sitekey="6LcR_RsTAAAAAN_r0GEkGBfq3L7KmU5JbPHJtwNp"
     data-s="AB2grfE8_kyMp3XYRuJo5c..."
     data-callback="onCaptchaSolved">
</div>

Aparece junto al data-sitekey de siempre, dentro del <div> del widget. Si tu código solo lee data-sitekey, se lo salta sin avisar.

Propiedades del token data-s

Propiedad Valor
Formato Cadena en Base64 de 200 a 500 caracteres
Vida útil De un solo uso, ligada a la carga actual
Alcance Específico de la sesión; no se reutiliza entre cargas
Obligatorio Sí, cuando está presente: sin él la resolución falla
Renovación Valor nuevo en cada carga o refresco

Qué ocurre por dentro cuando falta data-s

User triggers CAPTCHA (e.g., Google flags unusual search traffic)
    ↓
Google serves a CAPTCHA page with:

  - data-sitekey (site key, same for all Google search CAPTCHAs)
  - data-s (session token, unique per page load)
    ↓
reCAPTCHA widget initializes with both parameters
    ↓
Challenge completion generates a g-recaptcha-response token
    ↓
Token is submitted alongside the session reference
    ↓
Google validates token + session binding
    ↓
If data-s was not used during solving: "invalid-input-response" or silent failure

data-s funciona como un nonce del lado del servidor. Cuando falta, la secuencia es siempre la misma:

  1. El servicio de resolución genera el token sin conocer esa sesión.
  2. Recibes un g-recaptcha-response de aspecto válido.
  3. Google lo descarta con invalid-input-response o en silencio, sin pista sobre la causa.

Errores frecuentes que rompen la resolución

Casi todos los fallos con data-s caen en una de estas cinco casillas:

Error Síntoma Solución
Omitir data-s cuando el widget lo trae El token se rechaza en silencio Comprueba el atributo antes de enviar
Añadirlo cuando no existe Error del solver o rechazo Inclúyelo solo si está en el HTML
Reutilizarlo en varias cargas Token no válido Extrae un valor nuevo en cada carga
Codificar el valor en URL Parámetro mal formado Envía la cadena Base64 tal cual
Usar un data-s de hace minutos No coincide con la sesión Extrae y envía en la misma pasada

Cómo extraer data-s de la página

Se reduce a leer un atributo más del widget; la herramienta depende de cómo se construya la página:

  • HTML estático: requests y BeautifulSoup.
  • Node.js: axios y cheerio.
  • Widget inyectado por JavaScript: Selenium o Playwright.

Extracción con requests y BeautifulSoup

import requests
from bs4 import BeautifulSoup
import re

def extract_recaptcha_params(url):
    """Extract reCAPTCHA parameters including data-s from a page."""
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                      "AppleWebKit/537.36 (KHTML, like Gecko) "
                      "Chrome/120.0.0.0 Safari/537.36",
    }

    response = requests.get(url, headers=headers, timeout=15)
    soup = BeautifulSoup(response.text, "html.parser")

    # Find reCAPTCHA widget div
    widget = soup.find("div", class_="g-recaptcha")
    if not widget:
        # Try finding by data-sitekey attribute
        widget = soup.find(attrs={"data-sitekey": True})

    if not widget:
        return {"error": "No reCAPTCHA widget found"}

    params = {
        "sitekey": widget.get("data-sitekey"),
        "data_s": widget.get("data-s"),
        "callback": widget.get("data-callback"),
        "size": widget.get("data-size"),
        "has_data_s": widget.get("data-s") is not None,
    }

    return params

# Example: Google "unusual traffic" page
params = extract_recaptcha_params("https://www.google.com/sorry/index")
print(params)
# {
#   "sitekey": "6LfwuyUT...",
#   "data_s": "AB2grfE8_kyMp3...",
#   "has_data_s": True
# }

Extracción con Node.js y cheerio

Mismo recorrido del DOM: primer widget que aparezca y data-s en null cuando no está.

const axios = require("axios");
const cheerio = require("cheerio");

async function extractRecaptchaParams(url) {
    const { data: html } = await axios.get(url, {
        headers: {
            "User-Agent":
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " +
                "AppleWebKit/537.36 (KHTML, like Gecko) " +
                "Chrome/120.0.0.0 Safari/537.36",
        },
        timeout: 15000,
    });

    const $ = cheerio.load(html);
    const widget = $(".g-recaptcha, [data-sitekey]").first();

    if (widget.length === 0) {
        return { error: "No reCAPTCHA widget found" };
    }

    return {
        sitekey: widget.attr("data-sitekey"),
        dataS: widget.attr("data-s") || null,
        callback: widget.attr("data-callback") || null,
        hasDataS: !!widget.attr("data-s"),
    };
}

extractRecaptchaParams("https://www.google.com/sorry/index")
    .then(console.log);

Extracción con Selenium cuando la página es dinámica

Si el widget se inyecta por JavaScript, requests no verá nada: carga la página en un navegador y lee el atributo del DOM renderizado.

from selenium import webdriver
from selenium.webdriver.common.by import By

def extract_data_s_selenium(driver, url):
    """Extract data-s from a dynamically loaded reCAPTCHA page."""
    driver.get(url)

    # Wait for reCAPTCHA widget to load
    import time
    time.sleep(3)

    try:
        widget = driver.find_element(By.CSS_SELECTOR, ".g-recaptcha, [data-sitekey]")
        return {
            "sitekey": widget.get_attribute("data-sitekey"),
            "data_s": widget.get_attribute("data-s"),
        }
    except Exception:
        return {"error": "Widget not found"}

Cómo enviar data-s a la API de CaptchaAI

Cuando existe, viaja en el mismo POST a in.php que ya haces para un reCAPTCHA v2 normal:

  1. Extrae sitekey y data-s de la página.
  2. Envía la tarea con method=userrecaptcha y añade data-s junto a googlekey y pageurl.
  3. Consulta el resultado en res.php hasta recibir el token.

Ejemplo en Python

import requests
import time

API_KEY = "YOUR_API_KEY"

# Step 1: Extract parameters from the CAPTCHA page
sitekey = "6LfwuyUTAAAAAOAmoS0fdqijC2PbbdH4kjq62Y1b"
data_s = "AB2grfE8_kyMp3XYRuJo5c..."  # Extracted from data-s attribute
page_url = "https://www.google.com/sorry/index?continue=..."

# Step 2: Submit to CaptchaAI WITH data-s
submit = requests.post("https://ocr.captchaai.com/in.php", data={
    "key": API_KEY,
    "method": "userrecaptcha",
    "googlekey": sitekey,
    "pageurl": page_url,
    "data-s": data_s,  # Include data-s parameter
    "json": 1,
})

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

# Step 3: Poll for result
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"Token: {token[:60]}...")
        # Submit this token to the Google CAPTCHA form
        break

Ejemplo en Node.js

const axios = require("axios");

async function solveWithDataS(sitekey, dataS, pageUrl) {
    const API_KEY = "YOUR_API_KEY";

    // Submit with data-s
    const { data: submit } = await axios.post(
        "https://ocr.captchaai.com/in.php",
        new URLSearchParams({
            key: API_KEY,
            method: "userrecaptcha",
            googlekey: sitekey,
            pageurl: pageUrl,
            "data-s": dataS,
            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("Timeout");
}

Un extractor reutilizable para cualquier página

En lugar de bifurcar por sitio, encapsula la lógica en una clase que añada data-s solo cuando exista.

import requests
from bs4 import BeautifulSoup

class RecaptchaExtractor:
    """Extract reCAPTCHA parameters from any page."""

    def __init__(self, url, session=None):
        self.url = url
        self.session = session or requests.Session()
        self.params = None

    def extract(self):
        """Extract sitekey, data-s, and other parameters."""
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                          "AppleWebKit/537.36 (KHTML, like Gecko) "
                          "Chrome/120.0.0.0 Safari/537.36",
        }

        response = self.session.get(self.url, headers=headers, timeout=15)
        soup = BeautifulSoup(response.text, "html.parser")

        widget = soup.find(attrs={"data-sitekey": True})
        if not widget:
            raise ValueError("No reCAPTCHA widget found on page")

        self.params = {
            "sitekey": widget["data-sitekey"],
            "pageurl": self.url,
        }

        # Include data-s only if present
        data_s = widget.get("data-s")
        if data_s:
            self.params["data-s"] = data_s

        return self.params

    def build_solver_payload(self, api_key):
        """Build CaptchaAI submission payload with correct parameters."""
        if not self.params:
            self.extract()

        payload = {
            "key": api_key,
            "method": "userrecaptcha",
            "googlekey": self.params["sitekey"],
            "pageurl": self.params["pageurl"],
            "json": 1,
        }

        # Only include data-s when it exists
        if "data-s" in self.params:
            payload["data-s"] = self.params["data-s"]

        return payload


# Usage
extractor = RecaptchaExtractor("https://www.google.com/sorry/index?continue=...")
payload = extractor.build_solver_payload("YOUR_API_KEY")
# payload includes data-s only when present on the page
  • El mismo objeto sirve para un formulario corriente y para una página /sorry.
  • El payload lleva data-s solo si el widget lo traía en esa carga.

Preguntas frecuentes

¿Cómo compruebo en un minuto si una página necesita data-s?

Busca el <div class="g-recaptcha"> en las herramientas de desarrollo y mira si lleva el atributo data-s; desde la terminal, descarga el HTML y busca data-s=. Si no aparece, no lo envíes.

¿Es data-s lo mismo que el sitekey?

No, y confundirlos causa muchos fallos. El sitekey es público, estable e igual para todos; data-s cambia en cada carga y dejarlo fijo no funciona nunca.

¿Necesito un navegador headless para obtenerlo?

Depende. Si el widget viene en el HTML inicial, requests o axios bastan y consumen mucho menos. Si se inyecta por JavaScript, hace falta Selenium o Playwright.

¿Qué plan de CaptchaAI necesito para este tipo de trabajo?

Depende de la concurrencia, no del volumen: CaptchaAI factura por thread simultáneo, con resoluciones ilimitadas por thread. Para un script de monitorización, BASIC ($15/mes, 5 threads) sobra; con decenas de workers a la vez encaja mejor ADVANCE ($90/mes, 50 threads).


En resumen

data-s separa un token válido de uno que Google descarta en sus propias páginas de verificación. La regla operativa cabe en tres líneas:

  • Lee el atributo antes de cada envío.
  • Inclúyelo si existe y omítelo si no.
  • No lo reutilices entre cargas: con esa comprobación en tu extractor, la misma integración con la API de CaptchaAI sirve para un formulario cualquiera y para un reCAPTCHA de la Búsqueda de Google.

Artículos relacionados

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