Cuando un CAPTCHA de imagen vuelve mal resuelto, el culpable casi nunca es el motor OCR: es la imagen que enviaste o el parámetro que no enviaste. Reintentar sin cambiar nada solo repite el mismo error más caro.
Clasifica el fallo en una de tres familias: envío rechazado con un código ERROR_*, texto que no coincide con la imagen, o imagen demasiado degradada para leerse. Esta guía recorre las tres en ese orden, con el parámetro que corrige cada caso.
Triaje en 30 segundos
| Lo que ves | Familia del fallo | Dónde mirar |
|---|---|---|
Un código ERROR_* en la respuesta de in.php |
Envío rechazado | Cómo lees y codificas el archivo |
| Texto plausible, pero equivocado por uno o dos caracteres | Faltan sugerencias | numeric, regsense, min_len, calc |
| Texto sin ninguna relación con la imagen | Imagen degradada | Tamaño de captura, GIF animado, transparencia |
Las dos primeras filas se arreglan en tu código; la tercera, en cómo capturas.
La API rechaza el envío
Estos tres códigos llegan en la respuesta de in.php: la tarea ni siquiera entró en cola, así que el OCR no tuvo nada que ver. Se arreglan al leer y codificar el archivo.
ERROR_WRONG_FILE_EXTENSION: base64 mal formado
- Causa: base64 inválido o formato no compatible. Lo habitual es dejar el prefijo
data:image/png;base64,que devuelve el navegador. - Arreglo: codifica el archivo crudo y manda solo la cadena, sin el prefijo.
import base64
# Ensure proper encoding
with open("captcha.png", "rb") as f:
b64 = base64.b64encode(f.read()).decode()
# Don't include the data URI prefix
# WRONG: "data:image/png;base64,iVBOR..."
# RIGHT: "iVBOR..."
ERROR_ZERO_CAPTCHA_FILESIZE: el archivo llegó vacío
- Causa: guardaste un archivo de cero bytes, casi siempre porque la imagen exige la cookie de sesión y tu cliente la descargó sin ella.
- Arreglo: comprueba el tamaño antes de enviar y vuelve a capturar dentro de la misma sesión.
import os
# Check file size before submitting
if os.path.getsize("captcha.png") == 0:
print("Image file is empty — re-download")
# Re-capture the captcha
ERROR_TOO_BIG_CAPTCHA_FILESIZE: imagen por encima del límite
- Causa: la imagen supera el máximo admitido (normalmente 600 KB) porque envías todo el viewport en vez del recorte del CAPTCHA.
- Arreglo: recorta al elemento y optimiza sin perder nitidez en los trazos.
from PIL import Image
import io
img = Image.open("captcha.png")
# Reduce quality without losing text clarity
buffer = io.BytesIO()
img.save(buffer, format="PNG", optimize=True)
El texto vuelve mal: parámetros de sugerencia
Aquí se gana la mayor parte de la precisión: lo que ya sabes del CAPTCHA — solo dígitos, seis caracteres, distingue mayúsculas — hay que decirlo en el envío.
Confusión entre caracteres parecidos: 0/O, 1/l/I, 5/S
- Causa: el conjunto de caracteres está abierto y el solver duda entre glifos casi idénticos, captura tras captura.
- Solución: cierra el conjunto con
numeric.
# If captcha is digits only
response = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY, "method": "base64", "body": b64,
"numeric": 1, # 1 = digits only
"json": 1
})
# If captcha is letters only
response = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY, "method": "base64", "body": b64,
"numeric": 2, # 2 = letters only
"json": 1
})
El formulario distingue mayúsculas y minúsculas
- Qué ves: las letras son correctas, pero el formulario rechaza la respuesta igual.
- Causa: la respuesta vuelve en minúsculas por defecto y el formulario compara carácter a carácter.
- Solución: activa
regsense=1.
response = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY, "method": "base64", "body": b64,
"regsense": 1, # Case-sensitive
"json": 1
})
Sobran o faltan caracteres
- Qué ves: la respuesta llega con siete caracteres cuando el campo admite seis, o al revés.
- Causa: el ruido se lee como un carácter más, o dos glifos pegados se leen como uno.
- Solución: si la longitud es fija, decláralo con
min_lenymax_len.
# If you know the CAPTCHA is always 6 characters
response = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY, "method": "base64", "body": b64,
"min_len": 6,
"max_len": 6,
"json": 1
})
El CAPTCHA es una operación matemática
- Causa: recibes "3+7" en vez de "10": el OCR transcribe la expresión, no la evalúa.
- Solución: pide el cálculo con
calc=1.
response = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY, "method": "base64", "body": b64,
"calc": 1, # Compute the math expression
"json": 1
})
Referencia rápida de parámetros
Cada fila corresponde a uno de los síntomas anteriores.
| Parámetro | Cuándo usarlo | Efecto |
|---|---|---|
numeric=1 |
Solo dígitos | Elimina la confusión letra/dígito |
numeric=2 |
Solo letras | Elimina la confusión letra/dígito |
min_len / max_len |
Longitud conocida | Previene caracteres extra o faltantes |
regsense=1 |
Diferencia mayúsculas/minúsculas | Conserva el caso original |
calc=1 |
Expresión matemática | Devuelve la respuesta calculada |
phrase=1 |
Contiene espacios | Permite respuestas de varias palabras |
language=1 |
Texto cirílico | Usa el juego de caracteres correcto |
language=2 |
Texto latino | Usa el juego de caracteres correcto |
Cambia un parámetro por vez contra el mismo lote de capturas; con dos a la vez no sabrás cuál ayudó.
La imagen no da para más
Si ya cerraste el conjunto de caracteres y el texto sigue sin parecerse a la imagen, lo que capturas no lleva información suficiente. Estos tres casos cubren casi todo.
La capturas demasiado pequeña
- Problema: por debajo de unos 50 píxeles de altura los trazos se funden.
- Solución: busca una URL de origen con más resolución si la página la muestra reducida.
# Check for higher-res version
img_src = captcha_el.get_attribute("src")
# Some sites use ?size=small — try removing or changing the parameter
high_res_src = img_src.replace("size=small", "size=large")
El texto solo aparece en un fotograma
- Problema: algunos portales sirven GIF animados donde el texto solo es visible en ciertos cuadros.
- Solución: extrae los fotogramas y envía el que lleva el texto.
from PIL import Image
gif = Image.open("captcha.gif")
# Extract each frame and find the one with text
for i in range(gif.n_frames):
gif.seek(i)
gif.save(f"frame_{i}.png")
PNG con fondo transparente
- Problema: un PNG con canal alfa puede renderizarse sobre fondo oscuro y quedar ilegible.
- Solución: aplánalo sobre fondo blanco antes de codificarlo.
from PIL import Image
img = Image.open("captcha.png").convert("RGBA")
background = Image.new("RGBA", img.size, (255, 255, 255, 255))
background.paste(img, mask=img)
background.convert("RGB").save("captcha_white_bg.png")
Reporta las respuestas incorrectas
Si la respuesta llega mal pese a enviar la imagen y los parámetros correctos, repórtala con reportbad en vez de descartarla en silencio:
# Report bad answer
requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY,
"action": "reportbad",
"id": task_id
})
El reporte alimenta la precisión del solver y, en casos válidos, CaptchaAI puede compensar esa resolución. Hazlo solo con fallos reales: reportar aciertos ensucia la señal.
Un caso frecuente en portales públicos
Buena parte del volumen de OCR en el mercado hispanohablante viene de portales públicos: cita previa en España, trámites del SAT en México, AFIP en Argentina o los centros de visado BLS. Casi todos sirven texto en imágenes pequeñas y con ruido — el escenario exacto de numeric y min_len. Prueba esos dos parámetros contra veinte capturas guardadas antes de tocar nada más.
Si tus comprobaciones corren en paralelo, recuerda que CaptchaAI factura por thread concurrente, no por resolución: BASIC ($15/mes, 5 threads) cubre un monitor pequeño y ADVANCE ($90/mes, 50 threads) un pipeline con decenas de comprobaciones simultáneas. Respeta siempre los términos de servicio del sitio y la normativa de protección de datos aplicable (GDPR y LOPDGDD en España, LFPDPPP en México).
Preguntas frecuentes
¿Conviene reintentar automáticamente cuando el texto sale mal?
Sí, pero con un tope. Un reintento con una captura nueva resuelve el caso puntual; a partir del segundo el fallo es sistemático y toca revisar parámetros.
¿Qué hago si el CAPTCHA mezcla letras y números?
No uses numeric: cerrar el conjunto a un solo tipo empeora la lectura alfanumérica. Apóyate en min_len, max_len y regsense, que restringen sin descartar caracteres válidos.
¿Me conviene preprocesar la imagen antes de enviarla?
Solo cuando corrige un defecto concreto: aplanar transparencia, recortar o subir la resolución de origen. La binarización agresiva borra trazos finos y baja la precisión.
¿Los CAPTCHA de audio se resuelven por este mismo endpoint?
No. El flujo de audio es distinto y no se cubre con method=base64. Consulta la documentación de CaptchaAI para la compatibilidad vigente.
¿Los reintentos me suben la factura?
No: con facturación por thread y resoluciones ilimitadas dentro del plan, un reintento consume tiempo de thread, no un cargo aparte. Lo que sí baja es tu throughput.