# La API de password.es ya responde: cómo se usa

> Una llamada sin registro ni clave que devuelve contraseñas y los mismos números que ves en la web: bits, tiempo de crackeo y nivel. Incluido lo que todavía no hace, y por qué usarla no siempre es buena idea.

Publicado el 2026-08-31 por David Carrero · password.es
Original: https://password.es/blog/api-para-generar-contrasenas/

---

Este blog lleva meses repitiendo lo mismo: [tu contraseña no debería
salir de tu navegador](/blog/por-que-no-enviamos-tu-contrasena/). El
[generador](/) saca el azar de `crypto.getRandomValues()` en tu propia máquina y
el [comprobador](/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](/blog/que-son-los-bits-de-entropia/), 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](/blog/por-que-no-enviamos-tu-contrasena/).

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](/api/) cuenta para quién es,
qué responde hoy y qué no. La [referencia](/api/docs/) 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.*
