Explicaciones Técnicas

¿Qué es CAPTCHA de Solve Media?

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:

  1. El widget carga y muestra un mensaje publicitario como imagen.
  2. El usuario lee la frase de la marca y la escribe en el cuadro de entrada.
  3. La respuesta es verificada por los servidores de Solve Media.
  4. 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:

  1. Tu test detecta el widget en tu propia aplicación (formulario de QA, landing de staging, endpoint de preproducción).
  2. Envía a CaptchaAI los datos públicos del widget: para Solve Media, la imagen de la frase; para otros tipos, el sitekey y la URL.
  3. CaptchaAI resuelve el desafío y te devuelve el texto o el token válido.
  4. Tu test inyecta ese valor en el campo correspondiente y envía el formulario.
  5. 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

Valida tus integraciones CAPTCHA en entornos propios con CaptchaAI.

Los comentarios están deshabilitados para este artículo.