Cuando un bot de automatización se topa con un CAPTCHA, el flujo se detiene y hay que resolverlo a mano. La solución es delegar el desafío a una API: envías el CAPTCHA a CaptchaAI, recuperas el token resuelto y tu bot continúa solo. En esta guía montamos un framework reutilizable —en Python y Node.js— que resuelve reCAPTCHA, Cloudflare Turnstile e imagen CAPTCHA dentro de tareas repetitivas: envío de formularios, registro de cuentas, reservas y captura de datos.
Cómo delega un bot la resolución del CAPTCHA
El patrón es siempre el mismo, sin importar el tipo de CAPTCHA:
- Detectas el CAPTCHA en la página y extraes su sitekey.
- Envías el sitekey y la URL a CaptchaAI con el método adecuado.
- Sondeas el resultado hasta que la API devuelve el token resuelto.
- Inyectas el token en el formulario y completas el envío.
Ese ciclo —detectar, enviar, sondear, inyectar— es lo que vamos a encapsular en una sola clase.
Dónde aparecen los CAPTCHA en un bot de automatización
El método de la API depende del tipo de CAPTCHA que aparezca en cada flujo:
| Escenario | CAPTCHA habitual | Método de CaptchaAI |
|---|---|---|
| Envío de formularios | reCAPTCHA v2 | method=userrecaptcha |
| Registro de cuentas | reCAPTCHA v2/v3 | method=userrecaptcha |
| Portales de captura de datos | CAPTCHA de imagen | method=base64 |
| Reservas y citas | Cloudflare Turnstile | method=turnstile |
| Acceso a una API protegida | Cloudflare Challenge | method=cloudflare_challenge |
Cambia el método según la fila; el resto del flujo —enviar y sondear— es idéntico en todos los casos.
Un framework de bot reutilizable en Python
Encapsula el ciclo completo en una sola clase que todas tus tareas reutilicen:
import requests
import time
import logging
logger = logging.getLogger(__name__)
class CaptchaBot:
def __init__(self, api_key):
self.api_key = api_key
self.session = requests.Session()
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
})
def solve(self, method, **params):
"""Solve any CAPTCHA type."""
params["key"] = self.api_key
params["method"] = method
resp = requests.get("https://ocr.captchaai.com/in.php", params=params)
if not resp.text.startswith("OK|"):
raise Exception(f"Submit error: {resp.text}")
task_id = resp.text.split("|")[1]
logger.info(f"Task submitted: {task_id}")
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": self.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(f"Error: {result.text}")
raise TimeoutError("CAPTCHA solve timed out")
def submit_form(self, url, form_data, captcha_field="g-recaptcha-response",
site_key=None, captcha_method="userrecaptcha"):
"""Submit a form with CAPTCHA solving."""
if site_key:
if captcha_method == "userrecaptcha":
token = self.solve(captcha_method, googlekey=site_key, pageurl=url)
elif captcha_method == "turnstile":
token = self.solve(captcha_method, sitekey=site_key, pageurl=url)
form_data[captcha_field] = token
return self.session.post(url, data=form_data)
La clase resume tres piezas que reutilizarás en cualquier bot:
solveenvía el desafío ain.phpy sondeares.phpcada cinco segundos hasta obtener el token.submit_forminserta el token en el campo correcto antes de hacer el POST.- La
sessionreutiliza cookies y encabezados entre los pasos de un mismo flujo.
Ejemplo: bot que envía un formulario con reCAPTCHA
El caso más común es un formulario de contacto con reCAPTCHA v2. Le pasas la URL, los datos y el sitekey, y el bot resuelve el CAPTCHA justo antes de enviar:
bot = CaptchaBot("YOUR_API_KEY")
# Submit a contact form protected by reCAPTCHA
result = bot.submit_form(
url="https://example.com/contact",
form_data={
"name": "John Doe",
"email": "john@example.com",
"message": "Inquiry about your service"
},
site_key="6Le-wvkS...",
captcha_method="userrecaptcha"
)
print(f"Form submitted: {result.status_code}")
Ejemplo: bot de reserva de citas paso a paso
Muchos trámites —una cita previa en un portal público, una reserva en línea— reparten el proceso en varias pantallas y solo piden el CAPTCHA al final. El bot recorre cada paso y lo resuelve antes de confirmar. Automatiza únicamente flujos que tengas autorización para usar, respetando los términos de servicio del sitio:
def appointment_booking_bot(date, time_slot, user_info):
bot = CaptchaBot("YOUR_API_KEY")
# Step 1: Load booking page
page = bot.session.get("https://example.com/book")
# Step 2: Select date and time
resp = bot.session.post("https://example.com/book/select", data={
"date": date,
"time": time_slot
})
# Step 3: Fill personal info with CAPTCHA
result = bot.submit_form(
url="https://example.com/book/confirm",
form_data={
"name": user_info["name"],
"email": user_info["email"],
"phone": user_info["phone"],
"date": date,
"time": time_slot
},
site_key="6Le-wvkS...",
captcha_method="userrecaptcha"
)
return result.status_code == 200
# Run
success = appointment_booking_bot(
date="2025-02-15",
time_slot="10:00",
user_info={"name": "John Doe", "email": "john@example.com", "phone": "555-0100"}
)
Ejemplo: bot de captura de datos con CAPTCHA de imagen
En la captura masiva de datos el desafío suele ser un CAPTCHA de imagen: descargas la imagen, la codificas en base64 y la envías con method=base64. CaptchaAI devuelve el texto y el bot lo escribe en el campo:
import base64
def data_entry_bot(entries, captcha_image_url):
bot = CaptchaBot("YOUR_API_KEY")
for entry in entries:
# Load the form page
page = bot.session.get("https://portal.example.com/entry")
# Download and solve image CAPTCHA
img = bot.session.get(captcha_image_url)
img_b64 = base64.b64encode(img.content).decode()
captcha_text = bot.solve("base64", body=img_b64)
# Submit entry
resp = bot.session.post("https://portal.example.com/entry", data={
**entry,
"captcha": captcha_text
})
logger.info(f"Entry submitted: {resp.status_code}")
time.sleep(random.uniform(2, 5))
El mismo framework en Node.js
Si tu stack es JavaScript, el mismo patrón —enviar, sondear, devolver el token— se traslada directamente a Node.js con axios:
const axios = require("axios");
class CaptchaBot {
constructor(apiKey) {
this.apiKey = apiKey;
}
async solve(method, params) {
params.key = this.apiKey;
params.method = method;
const submit = await axios.get("https://ocr.captchaai.com/in.php", {
params,
});
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: this.apiKey, 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);
}
}
async submitForm(url, formData, siteKey, method = "userrecaptcha") {
const token = await this.solve(method, {
googlekey: siteKey,
pageurl: url,
});
formData["g-recaptcha-response"] = token;
return axios.post(url, new URLSearchParams(formData));
}
}
// Usage
const bot = new CaptchaBot("YOUR_API_KEY");
const result = await bot.submitForm(
"https://example.com/submit",
{ name: "John", email: "john@example.com" },
"6Le-wvkS..."
);
Buenas prácticas para que el bot no acabe bloqueado
Resolver el CAPTCHA es solo una parte del problema; el resto del tráfico también cuenta para que el bot funcione de forma estable:
- Usa el token antes de que caduque: envíalo dentro de los 120 segundos siguientes a la resolución.
- Espacia las solicitudes con pausas cortas entre acciones en lugar de dispararlas todas de golpe.
- Reparte el tráfico por salidas de red autorizadas cuando trabajes muchas veces sobre el mismo dominio.
- Revisa el HTML en busca de campos ocultos (tokens CSRF) que el formulario espera junto al token del CAPTCHA.
Cuántos threads necesita tu automatización
CaptchaAI factura por thread concurrente, no por resolución: el coste depende de cuántos CAPTCHA resuelvas en paralelo, no del total. Un bot secuencial que resuelve de uno en uno funciona de sobra con el plan BASIC ($15/mes, 5 threads); si lanzas varios bots a la vez o procesas colas grandes, ADVANCE ($90/mes, 50 threads) da margen para paralelizar. Como el precio mensual en USD es fijo, el coste resulta predecible para agencias y freelancers que facturan en monedas locales volátiles.
Problemas frecuentes y cómo resolverlos
| Síntoma | Qué revisar |
|---|---|
| El token es rechazado | Envíalo dentro de los 120 segundos; después caduca |
| El bot se bloquea con un token válido | Usa encabezados de navegador realistas y espacia las solicitudes con pausas |
| El formulario pide campos que no envías | Inspecciona el HTML en busca de campos ocultos (tokens CSRF) |
| Bloqueo por exceso de solicitudes | Añade pausas entre envíos y reparte el tráfico por salidas de red autorizadas |
Preguntas frecuentes
¿Necesito proxies para que mi bot no acabe bloqueado?
Depende del volumen. Para tareas puntuales no suele hacer falta; con muchas solicitudes al mismo sitio, reparte el tráfico por salidas de red autorizadas y añade pausas. CaptchaAI resuelve la capa del CAPTCHA, no gestiona tu red de salida.
¿Cuántos threads necesito para automatizar a gran volumen?
Tantos como CAPTCHA quieras resolver a la vez. Un thread procesa uno y queda libre al terminar: BASIC ($15/mes, 5 threads) cubre bots ligeros y ADVANCE ($90/mes, 50 threads) encaja mejor en cargas paralelas.
¿Durante cuánto tiempo es válido el token de CaptchaAI?
Poco: úsalo dentro de los 120 segundos siguientes a la resolución. Si tu bot tarda más entre resolver y enviar, el sitio lo rechazará y tendrás que resolver otro.
¿Puedo usar el mismo framework para varios tipos de CAPTCHA?
Sí. Cambias el método (userrecaptcha, turnstile, base64) y sus parámetros; el ciclo de enviar y sondear no cambia. Por eso conviene una sola clase reutilizable en lugar de un script por tipo.
¿Qué tipos de CAPTCHA puede resolver este framework?
Los que admite la API: reCAPTCHA v2 y v3, Cloudflare Turnstile y Cloudflare Challenge, GeeTest v3 e imagen/OCR y grid. hCaptcha y FunCaptcha no son compatibles hoy.