¿Tu automatización se topa con una cuadrícula de imágenes que hay que resolver celda a celda? La ruta más corta es enviar la imagen completa a CaptchaAI, recibir de vuelta las celdas correctas y reproducir los clics con Selenium. En esta guía lo montas de principio a fin en unas pocas líneas de Python o Node.js, sin depender del sistema de reCAPTCHA.
Cuándo conviene el método de imagen
No todas las cuadrículas se resuelven igual. Antes de escribir código, ubica en qué caso estás:
- Cuadrículas propias del sitio, ajenas a Google: una sola imagen partida en 3×3 o 4×4 donde marcas las celdas que encajan con una instrucción. Este es el caso que cubre el tutorial.
- Grillas de imagen independientes (una sola imagen estática): también van por aquí.
- reCAPTCHA de Google: no uses este método; ahí lo natural es resolver por token con
method=userrecaptcha.
Para las cuadrículas propias trabajas con el endpoint de imágenes de CaptchaAI (in.php con method=post y recaptcha=1), que es exactamente lo que montamos a continuación.
Antes de empezar
Ten a mano tres cosas:
- Clave API de CaptchaAI — la obtienes en captchaai.com.
- La imagen de la cuadrícula — una captura de pantalla o su base64 con la grilla completa.
- Python 3.7+ o Node.js 14+ — cualquiera de los dos vale para los ejemplos.
Paso 1: captura la cuadrícula como imagen
El primer paso es obtener la grilla completa en un formato que la API pueda leer. Tienes dos caminos según cómo esté servida la imagen.
Método A: captura de pantalla del contenedor del CAPTCHA
Cuando la grilla se renderiza en pantalla, lo más directo es recortar solo el contenedor del CAPTCHA y guardarlo como PNG.
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com/protected-form")
# Screenshot just the captcha container
captcha_element = driver.find_element(By.CSS_SELECTOR, "#captcha-container")
captcha_element.screenshot("captcha_grid.png")
Método B: extrae la imagen del atributo src
Cuando la imagen viaja en el atributo src, la lees directamente. Si viene como data:image ya está en base64; si es una URL, la descargas y la codificas.
import base64
import requests
captcha_img = driver.find_element(By.CSS_SELECTOR, ".grid-captcha img")
src = captcha_img.get_attribute("src")
if src.startswith("data:image"):
image_b64 = src.split(",")[1]
else:
image_data = requests.get(src).content
image_b64 = base64.b64encode(image_data).decode()
Paso 2: envía la imagen a CaptchaAI
Con la grilla en mano, la subes al endpoint de envío. Puedes mandar el archivo tal cual o su versión en base64; la respuesta te devuelve el request, que es el identificador de la tarea que usarás para consultar el resultado.
Con subida de archivo (Python)
Si tienes el PNG en disco, envíalo como archivo adjunto:
import requests
import time
API_KEY = "YOUR_API_KEY"
with open("captcha_grid.png", "rb") as f:
response = requests.post("https://ocr.captchaai.com/in.php",
data={
"key": API_KEY,
"method": "post",
"recaptcha": 1,
"json": 1
},
files={"file": f}
)
data = response.json()
task_id = data["request"]
print(f"Task: {task_id}")
Con base64 (Python)
Si ya la tienes codificada en memoria, mándala en el campo body y ahórrate el archivo:
response = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "post",
"body": image_b64,
"recaptcha": 1,
"json": 1
})
task_id = response.json()["request"]
Node.js
const axios = require('axios');
const fs = require('fs');
async function submitGridCaptcha(imagePath) {
const imageB64 = fs.readFileSync(imagePath).toString('base64');
const { data } = await axios.post('https://ocr.captchaai.com/in.php', null, {
params: {
key: 'YOUR_API_KEY',
method: 'post',
body: imageB64,
recaptcha: 1,
json: 1
}
});
return data.request;
}
Paso 3: consulta el resultado
La resolución no es instantánea, así que sondeas res.php cada pocos segundos hasta que la tarea pasa a status: 1. El bucle siguiente espera 5 segundos por vuelta, corta ante cualquier error real y aborta si se agota el tiempo.
def get_grid_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"]
if result.get("request") != "CAPCHA_NOT_READY":
raise Exception(f"Error: {result['request']}")
raise Exception("Timeout")
solution = get_grid_solution(task_id)
print(f"Solution: {solution}")
# Returns click coordinates or cell indices
Paso 4: aplica la solución en la página
La solución llega en uno de dos formatos, y cada uno se aplica distinto:
- Índices de celda (
2,5,6): haces clic en las celdas por su posición dentro de la grilla. - Coordenadas (
x=120,y=80;...): haces clic en píxeles concretos sobre el contenedor.
Comprueba cuál te ha devuelto la API y usa el bloque que corresponda.
Clic por índice de celda
# If solution returns cell indices (e.g., "2,5,6")
selected = [int(i) for i in solution.split(",")]
cells = driver.find_elements(By.CSS_SELECTOR, ".grid-cell")
for idx in selected:
cells[idx - 1].click()
time.sleep(0.2)
driver.find_element(By.CSS_SELECTOR, ".verify-button").click()
Clic por coordenadas
from selenium.webdriver.common.action_chains import ActionChains
# If solution returns coordinates (e.g., "x=120,y=80;x=250,y=200")
captcha_element = driver.find_element(By.CSS_SELECTOR, "#captcha-container")
actions = ActionChains(driver)
for coord in solution.split(";"):
parts = dict(p.split("=") for p in coord.split(","))
x, y = int(parts["x"]), int(parts["y"])
actions.move_to_element_with_offset(captcha_element, x, y).click()
actions.perform()
Errores frecuentes y cómo solucionarlos
| Error | Causa | Solución |
|---|---|---|
ERROR_WRONG_FILE_EXTENSION |
Formato de imagen no válido | Usa PNG o JPEG y verifica que el base64 sea válido |
ERROR_CAPTCHA_UNSOLVABLE |
Imagen demasiado pequeña o borrosa | Captura a máxima resolución |
| Celdas incorrectas seleccionadas | El formato de la solución no coincide | Revisa si la solución trae índices o coordenadas |
ERROR_TOO_BIG_CAPTCHA_FILESIZE |
La imagen supera el límite de tamaño | Redúcela por debajo de 600 KB |
Dónde te encontrarás estas cuadrículas
Este patrón aparece más de lo que parece. Si haces monitorización o QA de portales públicos de la región —una cita previa en España, un trámite en el SAT en México o una consulta en AFIP en Argentina—, es habitual toparte con cuadrículas de imagen personalizadas antes de acceder a un formulario. Lo mismo ocurre al validar flujos de checkout de prueba en marketplaces del tipo MercadoLibre. La mecánica no cambia: capturas la grilla, la resuelves por API y devuelves los clics a la página. Recuerda respetar los términos de servicio y la normativa de protección de datos aplicable en cada flujo.
Ejemplo completo y ejecutable
¿Quieres un proyecto funcional de punta a punta, con configuración del entorno, sondeo, reintentos y manejo de errores ya resueltos?
Ver el ejemplo completo y ejecutable en GitHub →
Preguntas frecuentes
¿Cuánto cuesta resolver cuadrículas de imagen con CaptchaAI?
CaptchaAI factura por threads concurrentes, no por resolución: cada thread admite resoluciones ilimitadas durante el mes. El plan BASIC cuesta $15/mes con 5 threads, y subes de tramo (STANDARD $30, 15 threads; ADVANCE $90, 50 threads) según tu volumen. Para un desarrollador o una agencia que factura en moneda local, un costo mensual predecible en USD suele salir mejor que pagar por cada resolución.
¿Necesito Selenium o puedo enviar la imagen directamente?
Selenium solo entra en juego si tienes que capturar la grilla desde un navegador o reproducir los clics. Si ya tienes la imagen —un PNG, un JPEG o su base64—, la envías a in.php con requests o axios sin abrir ningún navegador.
¿Este método sirve para hCaptcha o FunCaptcha?
No. CaptchaAI no es compatible con hCaptcha ni con FunCaptcha (Arkose Labs), así que este método no aplica a esos tipos. Cubre cuadrículas de imagen ajenas a reCAPTCHA y grillas de imagen independientes; para reCAPTCHA usa el flujo por token.
¿Funciona con cuadrículas dinámicas donde cambian los mosaicos?
En las cuadrículas dinámicas de reCAPTCHA, donde el mosaico se reemplaza al hacer clic, usa el método por token (method=userrecaptcha). El método de imagen resuelve una única grilla estática, así que no está pensado para mosaicos que se regeneran.
¿Cuánto influye la calidad de la imagen en el resultado?
Bastante. Las imágenes nítidas y a buena resolución dan los mejores resultados, y el tiempo medio de resolución ronda los 15 a 30 segundos. Captura siempre la cuadrícula completa y sin recortar para que el análisis interprete bien cada celda.