Si un formulario te pide escribir un eslogan de marca —algo como "Calidad en la que puedes confiar"— en lugar de descifrar letras deformadas, estás ante Solve Media. Es un CAPTCHA de tipo type-in con un origen poco habitual: nació como formato publicitario, no como barrera de seguridad, y la frase que muestra es un anuncio pensado para leerse con claridad.
Esa legibilidad es lo que lo vuelve sencillo de automatizar: al ser texto limpio dentro de una imagen, se resuelve con el mismo flujo de OCR que cualquier CAPTCHA de texto. Hoy es poco frecuente —la tecnología pasó a Permit.io / Radius Networks— pero sobrevive en sitios antiguos y conviene saber identificarlo cuando aparece en tu QA.
Cómo funciona el desafío
El ciclo que ve el usuario es corto y directo:
- El widget carga y muestra un mensaje publicitario como imagen.
- El usuario lee la frase de la marca y la escribe en el cuadro de entrada.
- La respuesta es verificada por los servidores de Solve Media.
- Si es correcto, se continúa con el envío del formulario.
┌─────────────────────────────────┐
│ │
│ "Taste The Rainbow" │
│ │
├─────────────────────────────────┤
│ Type the phrase: [____________] │
│ [Verify] │
└─────────────────────────────────┘
La diferencia frente a otros CAPTCHA no está en el mecanismo, sino en el contenido: la respuesta es una frase con sentido, no una cadena aleatoria.
Cómo detectar Solve Media en el HTML
Tu automatización necesita confirmar qué tiene delante. Solve Media deja huellas reconocibles: un script de api.solvemedia.com, un contenedor con prefijo adcopy- y campos ocultos donde viaja la respuesta.
<!-- Solve Media script -->
<script src="https://api.solvemedia.com/papi/challenge.script"></script>
<!-- Widget container -->
<div id="adcopy-outer"></div>
<!-- Hidden response fields -->
<input type="hidden" name="adcopy_challenge" />
<input type="hidden" name="adcopy_response" />
Cualquier elemento cuyo id empiece por adcopy- o contenga solvemedia es señal inequívoca, y basta para enrutar la página por la rama de OCR de tu pipeline.
Solve Media frente a un CAPTCHA de texto clásico
La legibilidad que lo hacía buen soporte publicitario también lo vuelve fácil para el OCR:
| Característica | Solve Media | CAPTCHA de texto estándar |
|---|---|---|
| Contenido del texto | Frases de marca, palabras en inglés | Caracteres aleatorios |
| Legibilidad | Alta (pensado para leerse) | Baja (pensado para dificultar) |
| Distorsión | Mínima | Elevada |
| Modelo de ingresos | Publicidad | Ninguno |
| Dificultad para el OCR | Baja (texto limpio) | Alta (texto distorsionado) |
| Experiencia de usuario | Mejor (palabras reconocibles) | Peor (difícil de leer) |
Resolverlo por OCR con CaptchaAI
Como Solve Media muestra texto dentro de una imagen, se resuelve con el método de Image/OCR CAPTCHA (method=base64 o method=post), el mismo de cualquier CAPTCHA de texto y sin endpoint dedicado. Capturas la imagen del widget, la envías a in.php y consultas res.php hasta que el texto esté listo. El único ajuste propio es phrase=1, porque las respuestas de Solve Media son frases con espacios.
import requests
import time
import base64
API_KEY = "YOUR_API_KEY"
# Capture the CAPTCHA image
# Method 1: Screenshot the widget
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com/page-with-solvemedia")
captcha_img = driver.find_element(By.CSS_SELECTOR, "#adcopy-puzzle-image img")
captcha_img.screenshot("solvemedia.png")
# Submit to CaptchaAI
with open("solvemedia.png", "rb") as f:
img_b64 = base64.b64encode(f.read()).decode()
response = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "base64",
"body": img_b64,
"phrase": 1, # Contains spaces (multi-word phrase)
"json": 1
})
task_id = response.json()["request"]
# 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:
text = result["request"]
print(f"Phrase: {text}")
break
# Type the phrase
driver.find_element(By.CSS_SELECTOR, "#adcopy_response").send_keys(text)
driver.find_element(By.CSS_SELECTOR, "form").submit()
En Node.js el enfoque es idéntico: envías la imagen en base64 y sondeas hasta recibir la frase.
const axios = require('axios');
const fs = require('fs');
async function solveSolveMedia(imagePath) {
const imageB64 = fs.readFileSync(imagePath).toString('base64');
const submit = await axios.post('https://ocr.captchaai.com/in.php', null, {
params: {
key: 'YOUR_API_KEY',
method: 'base64',
body: imageB64,
phrase: 1,
json: 1
}
});
const taskId = submit.data.request;
for (let i = 0; i < 30; i++) {
await new Promise(r => setTimeout(r, 5000));
const res = await axios.get('https://ocr.captchaai.com/res.php', {
params: { key: 'YOUR_API_KEY', action: 'get', id: taskId, json: 1 }
});
if (res.data.status === 1) return res.data.request;
}
throw new Error('Timeout');
}
Parámetros que marcan la diferencia
Un puñado de opciones de la petición OCR evita los fallos más comunes:
| Parámetro | Por qué importa |
|---|---|
phrase=1 |
Las frases de Solve Media contienen espacios |
numeric=2 |
Las frases suelen ser solo letras, sin dígitos |
Omitir min_len / max_len |
La longitud de la frase varía de un anuncio a otro |
| Respetar mayúsculas y minúsculas | Muchas implementaciones no distinguen el caso; envía la respuesta tal cual la devuelve el OCR |
Un escenario habitual: portal heredado en QA
Imagina que mantienes las pruebas de un portal público o un panel interno que arrastra tecnología de hace años y todavía incrusta Solve Media en un formulario. Tu suite de QA en staging.example.com falla de forma intermitente porque ese paso no está cubierto. Con el flujo anterior, tu test detecta el div#adcopy-outer, resuelve la imagen por OCR y rellena adcopy_response antes de enviar: validas el recorrido completo sin intervención manual.
Configuración reproducible del navegador
Usa exactamente la misma configuración de navegador en QA, staging y CI. Esto evita que un test funcione en local y falle en CI sin motivo aparente.
from selenium import webdriver
def make_driver(headless: bool = True) -> webdriver.Chrome:
options = webdriver.ChromeOptions()
if headless:
options.add_argument('--headless=new')
options.add_argument('--window-size=1280,800')
options.add_argument('--lang=es-ES')
return webdriver.Chrome(options=options)
Con el viewport, el idioma y el user-agent idénticos en todos los runners reduces la varianza y comparas resultados entre ejecuciones sin sorpresas.
Cómo encaja CaptchaAI en tu pipeline
El patrón de integración es siempre el mismo, sea cual sea el framework de pruebas:
- Tu test detecta el widget en tu propia aplicación (formulario de QA, landing de staging, endpoint de preproducción).
- Envía a CaptchaAI los datos públicos del widget: para Solve Media, la imagen de la frase; para otros tipos, el
sitekeyy la URL. - CaptchaAI resuelve el desafío y te devuelve el texto o el token válido.
- Tu test inyecta ese valor en el campo correspondiente y envía el formulario.
- Tu backend verifica la respuesta con el proveedor del CAPTCHA, igual que con un usuario real.
Este flujo se aplica solo a integraciones que tú controlas; no sirve para sortear protecciones de sitios de terceros.
Métricas y observabilidad
Instrumenta los pasos del CAPTCHA para detectar regresiones antes de que lleguen a producción:
- Tiempo de resolución por intento — desde la solicitud hasta la entrega del texto.
- Tasa de éxito por endpoint propio — verificaciones de backend que pasan respecto al total.
- Distribución de errores — por código (
ERROR_*, timeouts internos, fallos de red). - Latencia extremo a extremo — render, resolución del CAPTCHA y respuesta del backend.
Un par de hábitos ayudan a mantener estos números estables: usa una API key de CaptchaAI separada para QA (distinta de la de producción, para no mezclar métricas), define reintentos con backoff exponencial para no acumular trabajos pendientes durante caídas, y conserva trazas (logs, capturas, HAR) para reproducir los fallos intermitentes.
Solución de problemas
| Síntoma | Acción recomendada |
|---|---|
| El test no detecta el widget | Revisa selectores y tiempos en tu entorno de staging |
CaptchaAI devuelve ERROR_NO_SLOT_AVAILABLE |
Reintenta con backoff en tu pipeline interna |
| El backend rechaza la respuesta | Confirma que envías el texto en el campo adcopy_response correcto |
| El OCR devuelve una sola palabra | Asegúrate de enviar phrase=1 para conservar los espacios |
| Tiempos de resolución muy variables | Revisa la concurrencia y los límites de tu API key |
Preguntas frecuentes
¿Qué parámetros de OCR configuro para Solve Media?
Envía phrase=1 porque la respuesta es una frase con espacios, y numeric=2 porque casi siempre son solo letras. No fijes min_len ni max_len: la longitud cambia según el anuncio.
¿Puedo obtener la frase sin hacer OCR de la imagen?
A veces sí. En algunas implementaciones el texto viaja en el código fuente de la página o en la respuesta de la API. Revisa el campo oculto adcopy_challenge antes de capturar la imagen; si el texto está ahí, te ahorras el paso de OCR.
¿La verificación distingue mayúsculas y minúsculas?
Depende de la implementación. Muchas no diferencian el caso, pero no lo des por hecho: envía la respuesta tal como la devuelve el OCR y evita normalizarla salvo que confirmes que el servidor lo tolera.
¿Qué hago si el widget no muestra la imagen del anuncio?
Espera a que el script de api.solvemedia.com cargue del todo y a que el contenedor adcopy- sea visible antes de capturar la pantalla. Un screenshot prematuro devuelve una imagen en blanco que el OCR no puede leer.
Guías relacionadas
- Resolver CAPTCHA de imagen mediante la API
- Cómo funciona la resolución de CAPTCHA de imagen por OCR
- Errores frecuentes de OCR y cómo solucionarlos
Valida tus integraciones CAPTCHA en entornos propios con CaptchaAI.