Cuando el motor OCR de CaptchaAI te devuelve un texto que no coincide con lo que ves en pantalla, la culpa casi nunca es del modelo de resolución: es lo que le estás enviando. Un CAPTCHA de imagen se resuelve leyendo píxeles, así que si esos píxeles llegan recortados, comprimidos o mal codificados, la respuesta saldrá torcida por muchas veces que reintentes. Casi todos se diagnostican en minutos desde tu propio código: revisa primero la calidad y el recorte, luego la codificación y, al final, las pistas de tipo que pasas a la API.
Por qué un CAPTCHA de imagen devuelve una respuesta incorrecta
Antes de tocar nada, ubica el síntoma en una de estas causas. Cada fila apunta a una corrección distinta, y confundirlas hace perder tiempo.
| Causa | Frecuencia | Cómo corregirlo |
|---|---|---|
| Imagen recortada de más o de menos | Muy frecuente | Captura el elemento CAPTCHA completo |
| Baja resolución o exceso de compresión | Frecuente | Envía una imagen de mayor calidad |
| Codificación de la imagen incorrecta | Frecuente | Revisa la codificación base64 |
| Falta la pista de idioma o de tipo | Ocasional | Añade language o textinstructions |
| Imagen caducada o rotada | Ocasional | Captura una imagen nueva justo antes de resolver |
Con imágenes bien enviadas, CaptchaAI cubre más de 27.500 variantes de CAPTCHA de imagen con alta precisión; cuando algo falla, el punto débil suele estar en tu pipeline, no en el solver.
Comprueba la calidad de la imagen antes de enviarla
La comprobación más rentable es la más barata: validar la imagen en local antes de que consuma una llamada a la API. Este control revisa tres cosas que rompen el OCR: dimensiones por debajo del mínimo útil (unos 50x20 px), una imagen casi en blanco (recorte fallido o elemento sin cargar) y un peso fuera de rango. Mantén el archivo por debajo de 600 KB; más allá suele indicar que envías la página entera.
import base64
from io import BytesIO
from PIL import Image
def validate_captcha_image(image_path):
"""Check image quality before submitting to CaptchaAI."""
img = Image.open(image_path)
width, height = img.size
issues = []
# Minimum resolution
if width < 50 or height < 20:
issues.append(f"Too small: {width}x{height}px (min 50x20)")
# Check if mostly blank
pixels = list(img.getdata())
if img.mode == "RGB":
white_count = sum(1 for p in pixels if p[0] > 250 and p[1] > 250 and p[2] > 250)
else:
white_count = sum(1 for p in pixels if p > 250)
blank_ratio = white_count / len(pixels)
if blank_ratio > 0.95:
issues.append(f"Image appears blank ({blank_ratio:.0%} white)")
# File size check
img_bytes = BytesIO()
img.save(img_bytes, format="PNG")
size_kb = img_bytes.tell() / 1024
if size_kb < 1:
issues.append(f"File too small ({size_kb:.1f} KB) — may be empty")
if size_kb > 600:
issues.append(f"File too large ({size_kb:.0f} KB) — submit under 600 KB")
return issues
issues = validate_captcha_image("captcha.png")
if issues:
for issue in issues:
print(f"WARNING: {issue}")
else:
print("Image quality OK")
Codifica en Base64 sin corromper la imagen
Si la respuesta llega como un galimatías sin relación con la imagen, sospecha de la codificación. El error clásico es codificar el nombre del archivo (b"captcha.png") en lugar de los bytes que contiene: la API recibe basura y devuelve basura. Lee el archivo en binario, codifícalo y verifica el ida y vuelta con un assert.
import base64
def encode_captcha(image_path):
"""Properly encode a CAPTCHA image to base64."""
with open(image_path, "rb") as f:
raw = f.read()
encoded = base64.b64encode(raw).decode("ascii")
# Verify round-trip
decoded = base64.b64decode(encoded)
assert decoded == raw, "Base64 encoding corrupted the image"
return encoded
# WRONG — encoding a file path string
bad = base64.b64encode(b"captcha.png").decode() # Encodes filename, not image!
# CORRECT — encoding file contents
with open("captcha.png", "rb") as f:
good = base64.b64encode(f.read()).decode()
Mejora la lectura OCR con preprocesamiento
Cuando la respuesta se acerca pero no acierta (un carácter cambiado, una l por una 1), suele deberse a una imagen pequeña o de bajo contraste. Ampliar las imágenes muy chicas hasta unos 200 px de ancho, subir el contraste y aplicar un enfoque ligero le da al OCR más señal. No te pases: sobreprocesar introduce artefactos; resérvalo para los casos difíciles.
from PIL import Image, ImageFilter, ImageEnhance
from io import BytesIO
import base64
def preprocess_captcha(image_path):
"""Improve image quality for better OCR accuracy."""
img = Image.open(image_path)
# Convert to RGB if needed
if img.mode != "RGB":
img = img.convert("RGB")
# Upscale small images
width, height = img.size
if width < 200:
scale = 200 / width
img = img.resize(
(int(width * scale), int(height * scale)),
Image.LANCZOS,
)
# Increase contrast
enhancer = ImageEnhance.Contrast(img)
img = enhancer.enhance(1.5)
# Sharpen
img = img.filter(ImageFilter.SHARPEN)
# Convert to PNG bytes
buffer = BytesIO()
img.save(buffer, format="PNG")
return base64.b64encode(buffer.getvalue()).decode()
Añade pistas de tipo, longitud e idioma
Muchos fallos "casi correctos" desaparecen cuando le dices a la API qué esperar. Si el CAPTCHA es solo de dígitos, pasa numeric=1; si tiene entre 4 y 6 caracteres, acota con min_len y max_len para que no invente longitudes. Con textinstructions marcas detalles como la sensibilidad a mayúsculas, y con language indicas el alfabeto.
import requests
def solve_image(api_key, image_base64, **hints):
"""Submit image CAPTCHA with quality hints."""
data = {
"key": api_key,
"method": "base64",
"body": image_base64,
"json": 1,
}
# Add optional hints for better accuracy
if "language" in hints:
data["language"] = hints["language"] # 0=default, 1=Cyrillic, 2=Latin
if "textinstructions" in hints:
data["textinstructions"] = hints["textinstructions"]
if "numeric" in hints:
data["numeric"] = hints["numeric"] # 1=digits only, 2=letters only
if "min_len" in hints:
data["min_len"] = hints["min_len"]
if "max_len" in hints:
data["max_len"] = hints["max_len"]
resp = requests.post("https://ocr.captchaai.com/in.php", data=data, timeout=30)
return resp.json()
# Example: Digits-only CAPTCHA, 4-6 characters
result = solve_image(
"YOUR_API_KEY",
encoded_image,
numeric=1,
min_len=4,
max_len=6,
)
# Example: Case-sensitive text
result = solve_image(
"YOUR_API_KEY",
encoded_image,
textinstructions="Case-sensitive, enter exactly as shown",
)
Captura solo el elemento del CAPTCHA, no la página
Un recorte descuidado es la causa número uno de respuestas incorrectas. Si capturas toda la página y recortas por coordenadas fijas, cualquier cambio de layout deja el texto medio cortado. Mejor captura el propio elemento con Selenium: obtienes exactamente los píxeles del CAPTCHA, sin bordes ni relleno, y verificas que era visible comprobando el tamaño del PNG.
from selenium import webdriver
from selenium.webdriver.common.by import By
import base64
def capture_captcha_element(driver, selector):
"""Screenshot only the CAPTCHA element, not the full page."""
element = driver.find_element(By.CSS_SELECTOR, selector)
# Element screenshot (better than page crop)
png_bytes = element.screenshot_as_png
# Verify it's not empty
if len(png_bytes) < 500:
raise ValueError("Screenshot too small — element may not be visible")
return base64.b64encode(png_bytes).decode()
# Usage
driver = webdriver.Chrome()
driver.get("https://example.com")
image_b64 = capture_captcha_element(driver, "img#captchaImage")
Resuelve CAPTCHA dinámicos o rotativos sin que caduquen
Algunos portales rotan la imagen cada pocos segundos. Si capturas, esperas y luego envías, puedes resolver una imagen que ya no está en pantalla, y el sitio rechaza la respuesta aunque el texto fuera correcto. La regla es simple: captura y envía de inmediato. Como los CAPTCHA de imagen se resuelven rápido, un sondeo corto basta.
import time
def solve_with_fresh_image(driver, api_key, captcha_selector):
"""Capture and solve CAPTCHA immediately to avoid expiry."""
# Wait for CAPTCHA to load fully
time.sleep(2)
# Capture fresh
element = driver.find_element(By.CSS_SELECTOR, captcha_selector)
png_bytes = element.screenshot_as_png
body = base64.b64encode(png_bytes).decode()
# Submit immediately
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": api_key,
"method": "base64",
"body": body,
"json": 1,
}, timeout=30)
result = resp.json()
if result.get("status") != 1:
raise RuntimeError(result.get("request"))
task_id = result["request"]
# Poll — image CAPTCHAs solve fast
time.sleep(5)
for _ in range(12):
resp = requests.get("https://ocr.captchaai.com/res.php", params={
"key": api_key, "action": "get",
"id": task_id, "json": 1,
}, timeout=15)
data = resp.json()
if data.get("status") == 1:
return data["request"]
if data["request"] != "CAPCHA_NOT_READY":
raise RuntimeError(data["request"])
time.sleep(3)
raise TimeoutError("Image solve timeout")
Tabla de diagnóstico rápido
Cuando tengas un fallo concreto delante, usa esta tabla para ir directo a la corrección.
| Síntoma | Diagnóstico | Corrección |
|---|---|---|
| La respuesta es un galimatías | Codificación base64 incorrecta | Verifica la codificación de ida y vuelta |
| La respuesta se acerca pero falla | Baja calidad de imagen | Preprocesa: amplía, enfoca, sube contraste |
| El número de caracteres no cuadra | Faltan pistas de longitud | Añade min_len / max_len |
| Mezcla letras y dígitos | Falta la pista de tipo | Añade numeric=1 o numeric=2 |
| Devuelve una respuesta vacía | Imagen en blanco o corrupta | Valida la imagen antes de enviarla |
| Respuesta correcta pero el sitio la rechaza | Sensibilidad a mayúsculas | Añade textinstructions para el caso |
Un caso real: QA de un portal con CAPTCHA de imagen
Un equipo que automatiza pruebas de regresión sobre su propio entorno de staging —un flujo de cita previa que replica el de un portal público en España— veía que una de cada cinco ejecuciones fallaba con un texto casi correcto. No era el solver: el script capturaba la página completa y recortaba por coordenadas, y un banner intermitente desplazaba el CAPTCHA unos píxeles. Al pasar a la captura del elemento y validar el tamaño del PNG antes de enviarlo, los fallos desaparecieron. El coste es predecible en USD (el plan BASIC son $15/mes con 5 threads), algo que encaja con agencias y freelancers que facturan en monedas locales volátiles. Respeta siempre los términos de servicio y la normativa de protección de datos aplicable.
Preguntas frecuentes
¿Por qué CaptchaAI devuelve el texto correcto pero el formulario lo rechaza?
Casi siempre es sensibilidad a mayúsculas y minúsculas, o espacios sobrantes. Añade textinstructions indicando que se distinga el caso y compara carácter por carácter lo que envías con lo que espera el formulario.
¿Cómo distingo un problema de codificación de uno de calidad de imagen?
Mira la respuesta: si es un galimatías sin relación con la imagen, el fallo está en la codificación base64; si se parece pero cambia un carácter, es calidad de imagen y toca preprocesar.
¿Es necesario preprocesar todas las imágenes?
No. CaptchaAI lee bien los CAPTCHA de imagen estándar sin ningún preprocesamiento. Reserva el ampliado, el enfoque y el contraste para imágenes muy pequeñas o de bajo contraste; en el resto, procesar de más empeora la lectura.
¿Puedo reportar una respuesta incorrecta y recuperar el saldo?
Sí. Usa el endpoint reportbad con el ID de la tarea para informar de respuestas incorrectas. Ayuda a mejorar la precisión y puede acreditar saldo en tu cuenta.
¿Sirve de algo ampliar un CAPTCHA muy pequeño?
En imágenes por debajo de unos 200 px de ancho, sí: reescalar con un filtro de buena calidad y subir el contraste le da al OCR más detalle. Por encima de ese tamaño, ampliar ya no aporta.
Guías relacionadas
- Diagnóstico cuando cae la tasa de resolución
- Validación de parámetros y errores de solicitud
Resuelve tus CAPTCHA de imagen con precisión — prueba CaptchaAI.