Este blog lleva meses repitiendo lo mismo: tu contraseña no debería
salir de tu navegador. El
generador saca el azar de crypto.getRandomValues() en tu propia máquina y
el comprobador analiza lo que escribes sin enviarlo a ningún
sitio.
Y hoy publicamos una API que genera contraseñas en un servidor.
La contradicción es evidente y no vamos a disimularla: cada respuesta de la
API lleva un aviso diciendo exactamente eso. Pero hay sitios donde no hay
navegador —un script que aprovisiona cien cuentas, un servidor que genera una
credencial temporal, un asistente al que alguien le pide una contraseña y se la
inventa— y ahí la alternativa no es «el navegador»: es un random() mal elegido
o una cadena escrita a mano. Para eso está esto.
La llamada entera
Sin clave, sin registro, sin cabeceras. Esto es todo:
curl -X POST https://api.password.es/v1/generate
Devuelve una contraseña de 16 caracteres y su análisis. Ejecutado mientras se
escribía este párrafo: x*7$9,BUy9PPvOI?, sacada de un alfabeto de 89
caracteres, 103,6 bits de entropía, y un tiempo de crackeo de 2,5 × 10¹¹
años. Nivel 4 sobre 4.
Ninguno de esos números es nuevo. Salen del mismo motor que pinta el medidor de la portada: la misma fórmula de bits, el mismo modelo de ataque —10¹² intentos por segundo, sin conexión, hash rápido— y la misma escala de nivel que el comprobador. Si la web y la API dieran números distintos para la misma contraseña, uno de los dos estaría mintiendo.
Lo que devuelve, campo a campo
La respuesta trae passwords —siempre un array, aunque pidas una sola— y un
bloque analysis con length, pool, bits, log10_guesses,
crack_time_log10_seconds, crack_time, level, level_scale y ceiling.
Dos de esos merecen un párrafo. level_scale dice de dónde salió el nivel
(hoy siempre "time"), para que una divergencia futura entre la web y la API se
vea en el campo en vez de tener que deducirla comparando números. Y ceiling
dice si la cifra es exacta o un techo: en generate es siempre false,
porque la contraseña la ha hecho el servidor y sabe con qué alfabeto; en el
comprobador de la web no siempre puede serlo, porque ahí la contraseña la traes
tú.
Luego viene notice, el aviso, y un bloque _meta con el plan, el idioma que
se aplicó, los límites y un enlace a la documentación.
Las opciones
Todas opcionales, y con los nombres en inglés porque quien integra una API
escribe los campos tal cual: length (4–64, por defecto 16), count (1–20),
lower, upper, digits, symbols, exclude_ambiguous, no_repeats y
lang.
curl -X POST https://api.password.es/v1/generate -H 'content-type: application/json' -d '{"length":20,"exclude_ambiguous":true}'
Dos detalles que la documentación cuenta y que conviene saber antes de
integrarlo. exclude_ambiguous quita seis caracteres, no siete: descarta
0 O 1 I l | o, pero la barra vertical no estaba en el juego de símbolos para
empezar, así que el alfabeto baja de 89 a 83 y no a 82. Por eso el pool de la
respuesta dice 83. Y no_repeats no es una garantía absoluta: reintenta
diez veces, exactamente como el generador de la web, y a 64 caracteres se le
cuela una repetición contigua en torno al 0,1 % de las veces. Decirlo es más
útil que prometer lo contrario.
El idioma
Por defecto responde en inglés, que es lo que espera quien integra sin decir
nada. Se cambia con ?lang= en la URL, con "lang" en el cuerpo o con la
cabecera Accept-Language, y si coinciden gana el primero. Un idioma que no
existe no da error: cae a inglés.
Aquí hay una asimetría que no se adivina y por eso la API la declara: los
mensajes existen en inglés y español; los enlaces, en los dieciocho idiomas del
sitio. Pedir alemán te devuelve los enlaces en alemán y los mensajes todavía
en inglés. No hay que suponerlo: _meta.lang trae { "messages", "links" } con
lo que se aplicó a cada mitad.
El servidor MCP
MCP es el protocolo con el que un asistente —Claude, ChatGPT— usa herramientas
externas. https://api.password.es/mcp es un servidor MCP sin registro y sin
clave, y publica una sola herramienta: generate_password, con los mismos
parámetros de arriba.
Es la parte que más nos importa de todo esto, y no por lo técnico: un
asistente que no tiene de dónde sacar una contraseña se la inventa, y lo que
sale de ahí no es aleatorio, es lo que el modelo considera que parece una
contraseña. Con el servidor conectado deja de improvisar y devuelve una hecha
con crypto.getRandomValues(), con su análisis y con el aviso incluido en la
descripción de la herramienta y en cada resultado. Ese aviso es justo lo que el
asistente acaba leyéndole a quien pidió la contraseña, y por eso está ahí.
Si vienes de otros servidores MCP, un solo dato: este no tiene estado. El
POST se contesta con JSON y no abre ningún stream, no emite ni espera
Mcp-Session-Id, una notificación se responde con 202 sin cuerpo y el GET
devuelve 405. La especificación 2025-06-18 lo permite explícitamente, pero quien
espere sesiones y SSE va a depurar a ciegas si no se lo cuentan.
Lo que no hace
/v1/check devuelve 501. No es un despiste ni un endpoint a medio hacer:
está así a propósito y su propia respuesta explica por qué. Devolver los mismos
números que el comprobador exige el mismo motor de patrones que corre en la web,
y eso cuesta entre 11 ms y 3,6 s de CPU por petición según lo que le mandes. Con
un servicio sin registro y sin clave, esa horquilla es una decisión de producto
—dónde se pone el tope de longitud— que todavía no está tomada. Mientras tanto,
el 501 trae un checker_url que apunta al comprobador de la web en el idioma
que hayas pedido, que hace exactamente eso sin enviar nada.
Tampoco existen las cuentas, ni las claves, ni los planes. Y como no existen, no hay un solo enlace a un registro en toda la API: ni siquiera en el error de límite superado, que es donde todo el mundo lo pone.
Los límites
Uno, y es el que se aplica de verdad: 60 peticiones por minuto y por IP. No
hace falta creerse esta página, el número viaja dentro de cada respuesta en
_meta.limits.burst. Al pasarse sale un 429 con Retry-After y cabeceras
RateLimit-*.
En _meta verás además un bloque quota con los valores a null. Es el hueco
reservado para cuando haya cuentas, y va vacío a propósito: un límite
anunciado y no aplicado es peor que no anunciar ninguno, porque quien integra
lo respeta y programa contra un número que nadie está contando.
Y aun así, piénsalo dos veces
Una API que genera contraseñas es, en el fondo, un antipatrón: la contraseña viaja por la red y pasa por una máquina que no es la tuya. Que no guardemos nada no cambia la forma del problema, solo nuestra parte de él —y ya sabes lo que vale una promesa que no puedes comprobar.
Por eso el aviso no vive solo en esta página: viaja en cada respuesta, en el idioma que pidas. Y por eso este párrafo va aquí y no escondido al final de la documentación. Para una contraseña que vayas a usar tú, el generador de este sitio corre entero en tu navegador y no envía nada. La API es para lo otro: lo que ocurre sin nadie delante de una pantalla.
Los detalles están en dos páginas. Qué es la API cuenta para quién es,
qué responde hoy y qué no. La referencia tiene los nueve
parámetros, todos los campos explicados uno a uno, los códigos de error y los
límites; es la que se abre con el editor al lado. Y si lo que quieres es que lo
lea una máquina, está el openapi.json.
Fuentes: la propia API, verificada contra producción el 31 de agosto de 2026 —
POST /v1/generate, POST /v1/check (501), POST /mcp y GET /openapi.json ·
los números del análisis salen del mismo motor que el generador y el comprobador
de password.es · el modelo de ataque es 10¹² intentos por segundo, sin conexión
y con hash rápido, el mismo del resto del sitio · el servidor MCP implementa la
especificación del 2025-06-18 con transporte Streamable HTTP sin estado.
Foto de Stanislav Kondratiev · Pexels