Casos de Uso

Monitoreo de entradas de eventos con manejo de CAPTCHA

¿Quieres enterarte en el momento exacto en que se liberan entradas para un concierto agotado sin refrescar la página cada dos minutos? Un monitor automatizado lo hace por ti, pero las webs de entradas se defienden con reCAPTCHA v2, Cloudflare Turnstile y límites de solicitudes que frenan cualquier consulta repetida. La solución es delegar la parte del CAPTCHA: CaptchaAI lo resuelve mediante su API y tu monitor sigue comprobando la disponibilidad sin detenerse.

En esta guía montas, paso a paso, un monitor de disponibilidad en Python que detecta el CAPTCHA de cada página, lo resuelve con CaptchaAI y te avisa cuando el stock cambia. Está pensado para vigilar disponibilidad, no para automatizar la compra: respeta los términos de servicio de cada plataforma y la normativa de protección de datos aplicable.


Cómo encaja la resolución de CAPTCHA en el monitoreo

El diseño es un bucle sencillo que repites a intervalos regulares:

  • Configuras los eventos que te interesan.
  • Consultas cada página y compruebas si aparece un CAPTCHA.
  • Si aparece, lo resuelves con CaptchaAI antes de leer la disponibilidad.
  • Comparas el estado con la comprobación anterior y, si cambió, disparas una alerta.

Un caso concreto: un fan sigue la gira de un artista que pasa por Madrid, Ciudad de México y Buenos Aires, y quiere saber en cuál de esas fechas reaparecen entradas devueltas. En vez de vigilar tres pestañas a mano, un único monitor recorre las tres páginas cada pocos minutos.

Configure events → Check availability → CAPTCHA?
                                           ↓ Yes
                                      Solve via CaptchaAI → Retry
                                           ↓ No
                                      Parse availability → Changed?
                                                             ↓ Yes
                                                         Send alert

Lo que necesitas

Con estos tres elementos tienes lo justo para un monitor funcional:

  • Clave API de CaptchaAI, desde captchaai.com.
  • Python 3.8+ con la librería requests.
  • Proxy: una salida de red autorizada, recomendada para producción.
pip install requests

La salida de red autorizada no es imprescindible para empezar, pero marca la diferencia: las páginas de entradas bloquean con dureza las IP de centros de datos, y una IP residencial autorizada reduce cuántos CAPTCHA te encuentras.


Función auxiliar para resolver el CAPTCHA

El primer bloque es un ayudante reutilizable. La misma función sirve para reCAPTCHA v2 (userrecaptcha) y para Cloudflare Turnstile (turnstile): solo cambian los parámetros que le pasas.

import requests
import time

API_KEY = "YOUR_API_KEY"


def solve_captcha(method, params):
    """Generic CaptchaAI solver for any supported method."""
    params["key"] = API_KEY
    params["json"] = 1

    submit = requests.post("https://ocr.captchaai.com/in.php", data=params).json()

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

    task_id = submit["request"]
    initial_wait = 10 if method == "turnstile" else 20
    time.sleep(initial_wait)

    for _ in range(30):
        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 RuntimeError(f"Solve error: {result['request']}")
        time.sleep(5)
    raise TimeoutError("Solve timed out")

Lo que hace, en tres pasos:

  • Envía la tarea al endpoint in.php con tus parámetros y tu clave.
  • Espera un momento inicial: más corto para Turnstile, más largo para reCAPTCHA.
  • Sondea res.php hasta que el token está listo; mientras la respuesta sea CAPCHA_NOT_READY, sigue consultando.

El monitor de disponibilidad

Ahora la clase que hace el trabajo. Cada vez que consulta un evento, descarga la página, comprueba si hay un CAPTCHA en el HTML y, si lo hay, extrae el sitekey, lo resuelve y reenvía la solicitud con el token. Después analiza la disponibilidad y la compara con la última comprobación; si cambió, emite la alerta.

from datetime import datetime
import json


class TicketMonitor:
    def __init__(self, proxy=None):
        self.session = requests.Session()
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
        })
        if proxy:
            self.session.proxies = {
                "http": f"http://{proxy}",
                "https": f"http://{proxy}"
            }
        self.last_status = {}

    def check_event(self, event):
        """Check ticket availability for an event, solving CAPTCHAs if needed."""
        url = event["url"]
        response = self.session.get(url)

        # Handle CAPTCHA if detected
        if "g-recaptcha" in response.text or "recaptcha" in response.text:
            sitekey = self._extract_sitekey(response.text)
            if sitekey:
                token = solve_captcha("userrecaptcha", {
                    "method": "userrecaptcha",
                    "googlekey": sitekey,
                    "pageurl": url
                })
                response = self.session.post(url, data={
                    "g-recaptcha-response": token
                })

        elif "cf-turnstile" in response.text:
            sitekey = self._extract_turnstile_key(response.text)
            if sitekey:
                token = solve_captcha("turnstile", {
                    "method": "turnstile",
                    "sitekey": sitekey,
                    "pageurl": url
                })
                response = self.session.post(url, data={
                    "cf-turnstile-response": token
                })

        # Parse availability
        availability = self._parse_availability(response.text, event)

        # Check for changes
        event_key = event["name"]
        if event_key in self.last_status:
            if availability != self.last_status[event_key]:
                self._send_alert(event, availability)

        self.last_status[event_key] = availability
        return availability

    def _extract_sitekey(self, html):
        if 'data-sitekey="' in html:
            start = html.index('data-sitekey="') + 14
            end = html.index('"', start)
            return html[start:end]
        return None

    def _extract_turnstile_key(self, html):
        if 'data-sitekey="' in html:
            start = html.index('data-sitekey="') + 14
            end = html.index('"', start)
            return html[start:end]
        return None

    def _parse_availability(self, html, event):
        """Parse ticket availability. Customize per ticketing site."""
        available = "sold out" not in html.lower()
        return {
            "event": event["name"],
            "available": available,
            "checked_at": datetime.now().isoformat()
        }

    def _send_alert(self, event, availability):
        """Send availability change notification."""
        status = "AVAILABLE" if availability["available"] else "SOLD OUT"
        print(f"[ALERT] {event['name']}: {status}")

    def monitor_all(self, events):
        """Check all events and return results."""
        results = []
        for event in events:
            try:
                result = self.check_event(event)
                results.append(result)
                print(f"[OK] {event['name']}: {'available' if result['available'] else 'sold out'}")
            except Exception as e:
                print(f"[ERROR] {event['name']}: {e}")
        return results


# Usage
events = [
    {
        "name": "Concert - Madison Square Garden - Aug 15",
        "url": "https://example-tickets.com/event/12345"
    },
    {
        "name": "Basketball Finals - Game 7",
        "url": "https://example-tickets.com/event/67890"
    }
]

monitor = TicketMonitor(proxy="user:pass@proxy.example.com:8080")
results = monitor.monitor_all(events)

for r in results:
    print(json.dumps(r, indent=2))

Adaptar el análisis de disponibilidad

El método _parse_availability es el que adaptarás a cada web: aquí busca el texto "sold out", pero cada plataforma marca el estado a su manera, así que ajústalo a la estructura HTML real de la página que sigues.

Resultado esperado

[OK] Concert - Madison Square Garden - Aug 15: available
[OK] Basketball Finals - Game 7: sold out

Problemas frecuentes y cómo resolverlos

Problema Causa probable Cómo resolverlo
CAPTCHA en cada comprobación Misma IP y sin persistencia de sesión Reutiliza cookies y rota las IP de salida autorizadas
Bloqueo tras unas pocas consultas Límite de solicitudes (rate limiting) Amplía el intervalo entre comprobaciones y usa rotación de IP
Disponibilidad mal detectada Cambió la estructura de la página Ajusta el método _parse_availability
Resolución lenta del CAPTCHA Mucha carga en ese momento Añade lógica de reintento con retroceso exponencial

Programa las comprobaciones

Un monitor solo es útil si se ejecuta solo. La forma más directa en Linux o macOS es una tarea de cron que lance el script cada cierto tiempo y guarde la salida en un log.

# Check every 15 minutes
*/15 * * * * cd /path/to/project && python ticket_monitor.py >> /var/log/tickets.log 2>&1

Ajusta el intervalo a la demanda del evento: quince minutos van bien para seguimiento general, pero para lanzamientos muy solicitados querrás bajarlo. Ten presente que a mayor frecuencia, más CAPTCHA te encontrarás.


Preguntas frecuentes

¿Qué tipos de CAPTCHA resuelve CaptchaAI en las webs de entradas?

CaptchaAI resuelve mediante su API los tres que más aparecen en estas páginas:

  • reCAPTCHA v2
  • Cloudflare Turnstile
  • páginas de Cloudflare Challenge

Conviene saber que hCaptcha no está soportado: si una plataforma concreta lo usa, ese punto del flujo tendrás que cubrirlo de otra forma.

¿Este flujo compra las entradas automáticamente?

No. El monitor solo detecta si hay disponibilidad y te envía una alerta; la compra la haces tú, en la web y bajo las condiciones de la plataforma. Úsalo para seguir el stock de un evento, hacer QA de tu propio flujo o vigilar cambios de disponibilidad, siempre respetando los términos de servicio de cada sitio.

¿Cuánto cuesta mantener un monitor que consulta cada pocos minutos?

Depende de cuántos CAPTCHA resuelvas a la vez, no de cuántas veces consultes. CaptchaAI factura por thread concurrente con resoluciones ilimitadas dentro del plan, no por CAPTCHA resuelto. Un monitor que revisa los eventos de uno en uno rara vez necesita más de un thread activo, así que el plan BASIC ($15/mes, 5 threads) suele bastar. Es un costo mensual fijo en USD, cómodo de presupuestar.

¿Qué plan de CaptchaAI necesito si sigo muchos eventos en paralelo?

Cada resolución simultánea ocupa un thread, así que la elección depende de cuántas coincidan en el tiempo:

  • BASIC ($15/mes, 5 threads): comprobaciones en secuencia.
  • STANDARD ($30/mes, 15 threads): varios eventos en paralelo.
  • ADVANCE ($90/mes, 50 threads): decenas de comprobaciones simultáneas.

¿Cómo gestiono las salas de espera virtuales?

Las salas de espera (colas) son un mecanismo distinto del CAPTCHA. Tu monitor debe detectar la página de cola y esperar o reintentar más tarde; CaptchaAI resuelve el CAPTCHA que aparece después, cuando la cola te da paso.


Empieza a monitorear con CaptchaAI

Crea tu cuenta en captchaai.com y conecta tu clave API al monitor. Con el plan BASIC ($15/mes, 5 threads) tu flujo resuelve reCAPTCHA v2 y Cloudflare Turnstile de forma automática mientras comprueba la disponibilidad, y tú solo recibes la alerta cuando algo cambia.


Guías relacionadas

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