Um monitor de ingressos que verifica disponibilidade a cada poucos minutos esbarra cedo ou tarde num CAPTCHA — reCAPTCHA v2 na página do evento, Cloudflare Turnstile na fila virtual, ou os dois ao mesmo tempo. Sem resolver esse desafio, o script simplesmente para de funcionar assim que a plataforma de ingressos aumenta a verificação, o que costuma acontecer justo quando a demanda dispara. Este guia monta, em Python, um monitor que resolve esses CAPTCHAs pela API da CaptchaAI e avisa você assim que um ingresso volta a ficar disponível.
O fluxo é direto: verificar a página, resolver o CAPTCHA quando ele aparecer, reenviar a requisição e comparar o resultado com a última verificação. Tudo roda em segundo plano, agendado com cron.
Como o CAPTCHA entra no fluxo de monitoramento
Configure events → Check availability → CAPTCHA?
↓ Yes
Solve via CaptchaAI → Retry
↓ No
Parse availability → Changed?
↓ Yes
Send alert
O laço central é curto: quando não há CAPTCHA, o monitor só compara a disponibilidade lida com a última verificação salva; quando há, ele resolve, reenvia a mesma requisição com o token e só então segue para a comparação. É esse ponto de decisão — "CAPTCHA?" — que o restante do guia implementa.
O que você precisa antes de montar o monitor
- Chave de API da CaptchaAI — obtenha em captchaai.com
- Python 3.8+ — com a biblioteca
requests - Proxy — egress de rede autorizado recomendado, para reduzir bloqueios por volume de requisições
Instale a única dependência externa:
pip install requests
Função para resolver o CAPTCHA pela API da CaptchaAI
O monitor precisa de uma função genérica que envie a tarefa à CaptchaAI, aguarde o processamento e devolva o token — a mesma lógica serve tanto para reCAPTCHA v2 quanto para Cloudflare Turnstile, mudando apenas o method. O initial_wait é menor para o Turnstile porque ele costuma resolver mais rápido que o reCAPTCHA v2.
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")
O for de 30 tentativas com time.sleep(5) é um polling simples — funciona, mas trata todo desafio como se levasse o mesmo tempo.
Dica: em produção, prefira um backoff crescente (5 s, 10 s, 20 s…) em vez do intervalo fixo. Isso reduz consultas desnecessárias à API enquanto o CAPTCHA ainda está em fila de resolução, sem atrasar a resposta nos casos que resolvem rápido.
Classe TicketMonitor: verifica, resolve e avisa
A classe abaixo reaproveita a função solve_captcha acima. Ela mantém uma sessão HTTP por execução, detecta qual widget está presente na página (g-recaptcha ou cf-turnstile), resolve o desafio quando aparece e reenvia o token junto com a próxima requisição. Depois compara o resultado com a última verificação salva em memória e dispara um alerta apenas quando o status muda — assim você não recebe notificação repetida a cada execução do cron.
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))
Saída esperada:
[OK] Concert - Madison Square Garden - Aug 15: available
[OK] Basketball Finals - Game 7: sold out
Erros comuns e como corrigir
Antes de agendar o monitor para rodar sozinho, vale conhecer os quatro problemas mais comuns:
- CAPTCHA aparece em quase toda verificação — geralmente por falta de cookies persistentes entre requisições, com o mesmo IP repetido. Reaproveite a
requests.Session(), preserve cookies e faça rotação de proxy periodicamente. - Bloqueado após poucas verificações — sintoma de rate limit do site de ingressos. Aumente o intervalo entre checagens e distribua as requisições entre proxies.
- Status de disponibilidade sempre errado — a estrutura HTML da página mudou. Revise o método
_parse_availabilitycom o HTML atual do site. - Resolução de CAPTCHA demorando muito — fila alta no solucionador em horário de pico. Implemente retentativa com backoff e aumente o tempo de espera antes da primeira consulta.
Rodando em produção: agendamento com cron
Para monitoramento contínuo, agende a execução do script em vez de deixá-lo rodando em loop dentro de um único processo:
# Check every 15 minutes
*/15 * * * * cd /path/to/project && python ticket_monitor.py >> /var/log/tickets.log 2>&1
A latência até o site monitorado importa mais do que parece. Se o worker roda numa região distante da plataforma de ingressos — por exemplo, uma instância na Europa verificando um site hospedado nos EUA —, o RTT extra se soma ao tempo de resolução do CAPTCHA e pode custar a janela de disponibilidade num evento concorrido. Para quem opera a partir do Brasil, uma região como AWS sa-east-1 (São Paulo) reduz a latência para sites hospedados na América do Sul, mas para plataformas de ticketing americanas normalmente ainda compensa rodar o worker mais perto do próprio alvo. Meça o RTT real antes de decidir onde hospedar.
Perguntas frequentes
As dúvidas mais comuns de quem já colocou esse monitor para rodar em produção:
Quantas threads da CaptchaAI eu preciso para monitorar vários eventos?
Depende de quantos eventos você verifica ao mesmo tempo e com que frequência. Como referência:
- BASIC (US$ 15/mês, 5 threads) — cobre bem um punhado de eventos a cada 15 minutos.
- ADVANCE (US$ 90/mês, 50 threads) — dá margem para dezenas de eventos em alta frequência.
Cada CAPTCHA em resolução ocupa uma thread; assim que termina, ela fica livre para o próximo.
Com que frequência posso verificar sem levar bloqueio?
Depende do tipo de evento:
- Monitoramento geral — a cada 10 a 30 minutos.
- Eventos de alta demanda — a cada 2 a 5 minutos, aceitando mais CAPTCHAs nessa frequência.
O CaptchaAI resolve o CAPTCHA que aparece depois da sala de espera virtual?
Sim. A sala de espera em si não é um CAPTCHA.
É uma página de fila que seu monitor deve reconhecer e aguardar (ou tentar de novo depois) — o CAPTCHA aparece só ao sair dela, e é resolvido normalmente pela função
solve_captcha.
Preciso trocar de proxy a cada verificação?
Não necessariamente:
- O que mais reduz CAPTCHA é manter cookies e sessão entre requisições do mesmo evento.
- A rotação de proxy ajuda principalmente quando o mesmo IP começa a levar bloqueio por volume, não a cada checagem isolada.
Como sei se o site mudou o tipo de CAPTCHA que está usando?
Se check_event parar de encontrar g-recaptcha ou cf-turnstile no HTML e a análise de disponibilidade começar a falhar, é sinal de que a página mudou — vale logar esses casos separadamente para revisar o _parse_availability e o trecho de detecção do widget.
Comece a monitorar seus ingressos agora
Pegue sua chave de API em captchaai.com e coloque este monitor resolvendo CAPTCHA automaticamente. Os próximos passos, na ordem:
- Gere sua chave de API na CaptchaAI.
- Rode
solve_captchaisolado contra um evento de teste antes de agendar. - Coloque o
TicketMonitorno cron e acompanhe o log por um dia antes de aumentar a frequência.
Guias relacionados
Para continuar construindo em cima deste monitor: