Para resolver un BLS CAPTCHA por API necesitas tres cosas: las imágenes de la cuadrícula en Base64, el texto de la instrucción que dice qué seleccionar, y una llamada al método bls de CaptchaAI que te devuelve los índices correctos. Con esos índices, tu script hace clic en las imágenes y envía el formulario.
El BLS CAPTCHA aparece en los portales de citas de BLS International, el intermediario que tramita las solicitudes de visado de decenas de consulados y que recibe un enorme flujo de solicitantes latinoamericanos. Cuando automatizas la vigilancia de disponibilidad de un flujo que tú administras, este desafío es la barrera que interrumpe cada sesión: muestra entre tres y nueve imágenes y pide seleccionar las que coinciden con una frase como "selecciona todas las imágenes con un automóvil". Se parece a la cuadrícula de reCAPTCHA, pero usa imágenes propias e instrucciones personalizadas, así que no puedes reutilizar tal cual un solver genérico de reCAPTCHA.
El recorrido completo son cuatro etapas encadenadas:
- Extraer las imágenes y el texto de la instrucción de la página.
- Enviar todo a CaptchaAI con el método
bls. - Sondear el resultado hasta recibir los índices correctos.
- Hacer clic en esas imágenes y enviar el formulario.
Qué necesitas antes de empezar
| Elemento | Valor |
|---|---|
| Clave API de CaptchaAI | Desde tu panel en captchaai.com |
| Imágenes del BLS CAPTCHA | Codificadas en Base64 desde la página |
| Texto de instrucciones | La frase que indica qué imágenes seleccionar |
| Entorno | Python 3.7+ o Node.js 14+ |
Un par de detalles antes del código:
- CaptchaAI resuelve el BLS CAPTCHA de forma nativa con el método
bls, junto a reCAPTCHA v2/v3, Cloudflare Turnstile, GeeTest v3 y desafíos de imagen y texto. - La facturación es por threads con resoluciones ilimitadas: el plan BASIC ($15/mes, 5 threads) cubre un flujo de citas individual y puedes escalar cuando vigiles varios portales a la vez.
Paso 1: extrae las imágenes y la instrucción de la página
El BLS CAPTCHA muestra normalmente entre 3 y 9 imágenes con una instrucción de texto, por ejemplo "Selecciona todas las imágenes con un automóvil". En esta etapa capturas dos cosas de la página:
- El texto de la instrucción, que viajará en el campo
instructions. - Cada imagen convertida a Base64, sin el prefijo
data:image/...;base64,.
Con Selenium
from selenium import webdriver
from selenium.webdriver.common.by import By
import base64
import requests as req
driver = webdriver.Chrome()
driver.get("https://blsitalypakistan.com/appointment")
# Get instruction text
instruction = driver.find_element(By.CSS_SELECTOR, ".captcha-instruction").text
print(f"Instruction: {instruction}")
# Get all captcha images as base64
images = {}
captcha_imgs = driver.find_elements(By.CSS_SELECTOR, ".captcha-image img")
for i, img in enumerate(captcha_imgs, 1):
src = img.get_attribute("src")
if src.startswith("data:image"):
# Already base64
images[f"image_base64_{i}"] = src.split(",")[1]
else:
# Download and encode
img_data = req.get(src).content
images[f"image_base64_{i}"] = base64.b64encode(img_data).decode()
Con Puppeteer
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch({ headless: 'new' });
const page = await browser.newPage();
await page.goto('https://blsitalypakistan.com/appointment');
// Get instruction
const instruction = await page.$eval('.captcha-instruction', el => el.textContent);
// Get images as base64
const images = await page.$$eval('.captcha-image img', imgs =>
imgs.map((img, i) => ({
key: `image_base64_${i + 1}`,
value: img.src.startsWith('data:') ? img.src.split(',')[1] : null
}))
);
Paso 2: envía las imágenes a CaptchaAI
Reúne el texto de instrucciones y todas las imágenes en un mismo payload y envíalo al endpoint in.php con el método bls. La respuesta trae un task_id que necesitarás para consultar el resultado.
Python
import requests
import time
API_KEY = "YOUR_API_KEY"
payload = {
"key": API_KEY,
"method": "bls",
"instructions": instruction,
"json": 1
}
# Add each image (up to 9)
for key, value in images.items():
payload[key] = value
response = requests.post("https://ocr.captchaai.com/in.php", data=payload)
data = response.json()
if data.get("status") != 1:
raise Exception(f"Submit error: {data.get('request')}")
task_id = data["request"]
print(f"Task submitted: {task_id}")
Node.js
const axios = require('axios');
const FormData = require('form-data');
async function submitBLS(instruction, images) {
const params = {
key: 'YOUR_API_KEY',
method: 'bls',
instructions: instruction,
json: 1,
...Object.fromEntries(images.map(img => [img.key, img.value]))
};
const { data } = await axios.post('https://ocr.captchaai.com/in.php', null, { params });
if (data.status !== 1) throw new Error(data.request);
return data.request;
}
Paso 3: consulta el resultado con sondeo
El solver no responde al instante: hay que sondear el endpoint res.php cada pocos segundos hasta que el estado sea 1. Al terminar, devuelve los índices de las imágenes correctas en formato "1,3,5".
Python
def get_bls_solution(task_id):
for _ in range(30):
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:
return result["request"] # e.g., "1,3,5" (image indices)
if result.get("request") != "CAPCHA_NOT_READY":
raise Exception(f"Error: {result.get('request')}")
raise Exception("Timeout")
solution = get_bls_solution(task_id)
print(f"Select images: {solution}") # e.g., "1,3,5"
Recibir CAPCHA_NOT_READY en las primeras vueltas es normal: significa que el desafío sigue en la cola. Cualquier otra respuesta distinta sí debes tratarla como error.
Paso 4: haz clic en las imágenes y envía el formulario
Con la lista de índices, recorre los elementos de la cuadrícula y haz clic en los que corresponden. La API numera desde 1, así que resta uno para el índice base cero del navegador.
# Parse the solution indices
selected = [int(i) for i in solution.split(",")]
# Click each correct image
captcha_imgs = driver.find_elements(By.CSS_SELECTOR, ".captcha-image img")
for idx in selected:
captcha_imgs[idx - 1].click() # Convert 1-based to 0-based
time.sleep(0.3) # Small delay between clicks
# Submit the form
driver.find_element(By.CSS_SELECTOR, ".captcha-submit").click()
Errores frecuentes y cómo resolverlos
Antes de juntar todo en un solo script, conviene reconocer los fallos que más aparecen en producción:
| Error | Causa | Qué hacer |
|---|---|---|
ERROR_BAD_PARAMETERS |
Faltan las instrucciones o las imágenes | Incluye tanto el texto de la instrucción como al menos una imagen |
ERROR_CAPTCHA_UNSOLVABLE |
Imágenes borrosas o irreconocibles | Captura imágenes de mayor calidad y verifica que la codificación Base64 sea correcta |
| Imágenes incorrectas seleccionadas | Orden de imágenes equivocado | Numera las imágenes en el orden real de visualización |
| Selección rechazada | Las imágenes cambiaron tras la extracción | Extrae y envía las imágenes de inmediato, en la misma sesión |
Ejemplo completo en Python
Este script encadena las cuatro etapas —extraer, enviar, sondear y hacer clic— en un flujo continuo.
import requests
import time
import base64
from selenium import webdriver
from selenium.webdriver.common.by import By
API_KEY = "YOUR_API_KEY"
driver = webdriver.Chrome()
driver.get("https://blsitalypakistan.com/appointment")
# 1. Extract instruction and images
instruction = driver.find_element(By.CSS_SELECTOR, ".captcha-instruction").text
captcha_imgs = driver.find_elements(By.CSS_SELECTOR, ".captcha-image img")
payload = {"key": API_KEY, "method": "bls", "instructions": instruction, "json": 1}
for i, img in enumerate(captcha_imgs, 1):
src = img.get_attribute("src")
if src.startswith("data:image"):
payload[f"image_base64_{i}"] = src.split(",")[1]
# 2. Submit to CaptchaAI
resp = requests.post("https://ocr.captchaai.com/in.php", data=payload).json()
task_id = resp["request"]
# 3. Poll for solution
for _ in range(30):
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:
selected = [int(i) for i in result["request"].split(",")]
break
# 4. Click correct images and submit
for idx in selected:
captcha_imgs[idx - 1].click()
time.sleep(0.3)
driver.find_element(By.CSS_SELECTOR, ".captcha-submit").click()
print("CAPTCHA solved!")
driver.quit()
Automatiza solo flujos de citas que tú administras y respeta los términos de servicio del portal y la normativa de protección de datos aplicable (GDPR y LOPDGDD en España, LFPDPPP en México y normas equivalentes en la región).
Proyecto ejecutable completo
¿Quieres un proyecto funcional con configuración del entorno, sondeo, reintentos y manejo de errores ya resueltos? El repositorio de ejemplos incluye todo listo para clonar y ejecutar.
Consulta el ejemplo ejecutable completo en GitHub →
Preguntas frecuentes
¿La API de CaptchaAI resuelve el BLS CAPTCHA de cualquier centro de visas?
Sí. El método bls trabaja sobre las imágenes y el texto que le envías, no sobre la URL, así que no depende del consulado ni del país concretos. Tú te encargas de la extracción y la API resuelve la cuadrícula.
¿Por qué se rechaza mi selección aunque los índices sean correctos?
Casi siempre porque las imágenes cambiaron entre la extracción y el envío. El BLS CAPTCHA regenera la cuadrícula por sesión, así que extrae, resuelve y haz clic en un único ciclo sin recargar la página. Si tardas demasiado, la instrucción ya apunta a otras imágenes.
¿Cuánto cuesta resolver BLS CAPTCHA a volumen alto?
CaptchaAI factura por threads con resoluciones ilimitadas dentro del mes, no por CAPTCHA resuelto. El plan BASIC ($15/mes, 5 threads) cubre un flujo individual; si vigilas varios portales en paralelo, STANDARD ($30/mes, 15 threads) te da más resoluciones simultáneas.
¿Necesito Selenium o me basta con solicitudes HTTP?
Para la parte de resolución no necesitas Selenium. Si consigues leer las imágenes y la instrucción con solicitudes HTTP directas, la API solo requiere las imágenes en Base64 y el texto. Selenium sigue siendo útil cuando la página arma la cuadrícula con JavaScript y hace falta un navegador real para renderizarla.