Inicio Medel Captcha
Estado del servicio: operativo

CAPTCHA libre,
sin keys, sin fricción.

Protege tus formularios contra bots en 30 segundos. Sin registros, sin tracking, sin cuotas. Self-hosted por Medel Platforms, basado en Proof-of-Work asimétrico.

↑ Lazy: el widget NO consume CPU hasta que pulses el input. Pruébalo.
51
Retos emitidos hoy
11
Verificaciones completadas
5
Tokens consumidos
Cómo funciona

Invisible para humanos. Caro para bots.

1

El servidor emite un reto

Cuando el widget se carga, pide a Medel Captcha un challenge: una semilla aleatoria + un nivel de dificultad. Coste para el servidor: 1 INSERT + 2 ms.

2

El navegador resuelve un PoW

En un WebWorker (sin bloquear la UI), el navegador busca un nonce tal que sha256(semilla + nonce) empiece por N ceros. Para un usuario tarda ~0.5-2s. Para un bot a escala, multiplica su coste de CPU.

3

Token single-use de 2 minutos

El servidor verifica el PoW y emite un token de un solo uso, válido 2 minutos. Tu backend lo verifica con una llamada antes de procesar el form.

Instalación

2 líneas de HTML.

Copia el snippet y pégalo en tu formulario. Sin npm, sin keys, sin configuración.

HTML
<form action="/contact" method="POST">
    <input name="email" required>
    <textarea name="message" required></textarea>

    <div class="medel-captcha"></div>

    <button type="submit">Enviar</button>
</form>

<script src="https://medel.es/captcha.js" async defer></script>
Lazy por defecto 0 ms de CPU hasta que el usuario toque el form.
Sin sitekey ni secret Pega el script y ya. Igual que reCAPTCHA pero sin registros.
Drop-in reCAPTCHA Acepta también class="g-recaptcha" y rellena g-recaptcha-response. Migra sin tocar tu backend.
Verificación server-side

Antes de procesar el form, verifica el token.

En tu servidor, al recibir el POST del formulario, llama al endpoint de verificación con el token que vino en el campo medel_captcha_token. El token es single-use: se invalida tras la primera llamada.

<?php
$token = $_POST['medel_captcha_token'] ?? '';

$ch = curl_init('https://medel.es/api/captcha/verify');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json'],
    CURLOPT_POSTFIELDS     => json_encode(['token' => $token]),
]);
$res = json_decode(curl_exec($ch), true);
curl_close($ch);

if (empty($res['ok'])) {
    http_response_code(400);
    exit('Captcha verification failed: ' . ($res['error'] ?? 'unknown'));
}

// ✅ Captcha verificado. Procede a procesar el formulario.
const token = req.body.medel_captcha_token;

const res = await fetch('https://medel.es/api/captcha/verify', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ token }),
});
const data = await res.json();

if (!data.ok) {
    return res.status(400).json({ error: 'Captcha failed', reason: data.error });
}

// ✅ Captcha verificado. Procede a procesar el formulario.
import requests

token = request.form.get('medel_captcha_token', '')

res = requests.post(
    'https://medel.es/api/captcha/verify',
    json={'token': token},
    timeout=5,
)
data = res.json()

if not data.get('ok'):
    abort(400, f"Captcha failed: {data.get('error')}")

# ✅ Captcha verificado. Procede a procesar el formulario.
curl -X POST 'https://medel.es/api/captcha/verify' \
  -H 'Content-Type: application/json' \
  -d '{"token":"EL_TOKEN_DEL_FORM"}'

# Respuesta OK:
# {"ok":true,"verified_at":"2026-06-22 12:24:56"}

# Respuesta error:
# {"ok":false,"error":"already_used"}
API

Referencia completa

GET https://medel.es/api/captcha/challenge

Emite un nuevo reto Proof-of-Work. Lo llama el widget al cargarse; tu servidor no lo necesita directamente.

Respuesta 200:
{
  "ok": true,
  "challenge_token": "21091047befb5f136b6c6525119b8eb0",
  "seed": "7bb242e980f302742c743370955afef1",
  "difficulty": 4,
  "expires_in": 300
}
Errores comunes:
  • banned — la IP está baneada 24h por abuso.
  • rate_limit_ip — más de 100 challenges/hora desde esta IP.
  • rate_limit_origin — más de 5000/hora desde tu dominio.
POST https://medel.es/api/captcha/solve

El widget envía el nonce encontrado. Lo llama el widget; tu servidor no lo necesita.

Body JSON:
{
  "challenge_token": "21091047befb5f136b6c6525119b8eb0",
  "nonce": "1841"
}
Respuesta 200:
{
  "ok": true,
  "token": "391faafa6b881fc4e174d046fd2eb9f6",
  "expires_in": 120
}
Compatibilidad

Funciona en el 98% de los sitios sin tocar nada.

Funciona automáticamente en:

  • Cualquier sitio servido por HTTPS (requisito de la WebCrypto API).
  • Navegadores modernos: Chrome/Edge 60+, Firefox 60+, Safari 11+ (lanzados desde 2017).
  • localhost durante desarrollo (los navegadores tratan localhost como contexto seguro).
  • Webs con CSP moderada que permita scripts de terceros.
  • Formularios server-rendered tradicionales Y SPAs (React/Vue/Svelte) — el script detecta widgets añadidos dinámicamente.

Si tu sitio tiene Content Security Policy estricta

Añade estas directivas mínimas a tu CSP. No requiere cambios en tu backend ni en el resto del HTML:

Content-Security-Policy:
  script-src  'self' https://medel.es;
  connect-src 'self' https://medel.es;
  style-src   'self' https://medel.es;

El widget NO usa WebWorkers (CSP-paranoia común), NO inyecta <style> inline (CSS desde archivo externo), y NO requiere unsafe-eval. Cero conflictos con políticas de seguridad serias.

No funcionará en estos casos (edge):

  • Sitios sólo HTTP (sin TLS). WebCrypto exige contexto seguro. Solución: usa HTTPS — tu sitio lo necesita en 2026 igual.
  • JavaScript deshabilitado. Limitación de cualquier captcha moderno. Como fallback, mantén un honeypot HTML puro.
  • Bloqueadores agresivos. Algunas extensiones podrían bloquear el script third-party. Es raro en captchas (suelen estar en allowlist).
Privacidad

Lo que NO guardamos.

No usamos cookies.
No hacemos fingerprinting del navegador.
No vendemos ni compartimos datos.
Sí guardamos la IP del cliente durante 24h, sólo para rate-limit y ban automático contra abuso. Se purga después.
Sí registramos el dominio (Origin/Referer) que hace la petición para estadísticas agregadas. Sin asociarlo a IPs concretas.
FAQ

Preguntas frecuentes

¿Realmente es gratis y sin keys?
Sí. Sin registro, sin email, sin tarjeta. Cualquiera puede integrarlo copiando el snippet. Los límites son por IP y por dominio, no por cuenta.
¿Cómo evitan el abuso del servicio?
Rate limit por IP (100/hora) y por dominio (5000/hora) en una ventana deslizante. Si una IP supera 10x el límite, se bannea automáticamente durante 24h. Sin pedir más al integrador.
¿Funciona sin JavaScript?
No. Medel Captcha necesita JS porque el Proof-of-Work se resuelve en el navegador. Para usuarios sin JS, recomendamos un fallback de honeypot HTML puro en tu form.
¿Por qué Proof-of-Work y no un puzzle?
PoW es invisible para el usuario (no tiene que hacer nada). Puzzles tipo "selecciona los semáforos" son costosos de generar y molestos. PoW es matemático y resistente a ML.
¿Puede un bot resolver el PoW?
Sí, pero costará. A nivel individual es trivial; a escala (10k bots intentando) consume CPU caro. El sistema sube la dificultad automáticamente cuando detecta sospecha desde una IP.
¿Se puede usar en formularios con AJAX?
Sí. El campo medel_captcha_token se rellena automáticamente en el form. Si lo envías por fetch/axios, el campo viaja con el resto del FormData.
¿Funciona en iOS/Safari/Firefox?
Sí. Usa WebCrypto API (SHA-256 nativo) y WebWorkers, soportados en todos los navegadores modernos desde 2018.
¿Y si Medel Captcha se cae?
El widget muestra "No se pudo verificar" y bloquea el submit por defecto. Como integrador puedes decidir bypass o no en tu backend si el verify falla por timeout.

Pega el snippet y olvídate de los bots.

Empezar ahora — es gratis