# Referencia de la API de contraseñas · password.es

> Todos los parámetros de POST /v1/generate, cada campo de la respuesta, los códigos de error y los límites de api.password.es. Con un ejemplo en curl que funciona.

password.es · Original: https://password.es/api/docs/

---

# Referencia de la API de contraseñas

La referencia de `api.password.es`: cada parámetro, cada campo de la respuesta, cada código de error y los límites. Los ejemplos se pegan en un terminal tal cual.

Si lo que buscas es qué es esto, para quién y cuándo *no* conviene usarlo, empieza por [la página de la API](https://password.es/api/).

## Los endpoints

Todo cuelga de `https://api.password.es`. Dos responden y uno todavía no, y el que no lo dice en su propia respuesta.

- POST/v1/generate Genera una o varias contraseñas y devuelve su análisis.
- GET/openapi.json La descripción de la API, en OpenAPI 3.1.
- POST/mcp Servidor MCP para asistentes. Una herramienta: `generate_password`.
- POST/v1/check 501 Todavía no existe. El error explica qué falta y adónde ir mientras tanto.

## Generar una contraseña

El cuerpo es opcional: sin él salen 16 caracteres con los cuatro tipos activados. Con él, lo que pidas.

La petición

```
curl -X POST https://api.password.es/v1/generate \
  -H 'content-type: application/json' \
  -d '{"length":20,"exclude_ambiguous":true}'
```

La respuesta

```
{
  "passwords": [
    "P#f.aK+w4pcsx]}Gx;*>"
  ],
  "analysis": {
    "length": 20,
    "pool": 83,
    "bits": 127.50078862693852,
    "log10_guesses": 38.08053185185735,
    "crack_time_log10_seconds": 26.08053185185735,
    "crack_time": {
      "value": "3.8 × 10¹⁸",
      "unit": "years"
    },
    "level": 4,
    "level_scale": "time",
    "ceiling": false
  },
  "notice": "Generated on someone else's machine, which is a security antipattern even though we store nothing. For a password you will actually use, the generator at https://password.es/en/ runs entirely in your browser and sends nothing.",
  "_meta": {
    "plan": "anonymous",
    "lang": {
      "messages": "en",
      "links": "en"
    },
    "limits": {
      "burst": {
        "limit": 60,
        "window_seconds": 60
      }
    },
    "quota": {
      "limit": null,
      "remaining": null,
      "reset": "2026-09-01T00:00:00.000Z"
    },
    "docs": "https://password.es/api/"
  }
}
```

## Los parámetros

Todos son opcionales. La tabla va en inglés y es idéntica en los dieciocho idiomas, a propósito: quien integra una API escribe los nombres de campo tal cual, y dieciocho traducciones de `exclude_ambiguous` serían deuda, no alcance.

- Field
- Type
- Default
- Notes
- length
- integer 4–64
- 16
- How many characters. The same range as the generator on this site.
- count
- integer 1–20
- 1
- How many passwords to return. `passwords` is always an array, including with `count` 1.
- lower
- boolean
- true
- Include a–z (26 characters).
- upper
- boolean
- true
- Include A–Z (26 characters).
- digits
- boolean
- true
- Include 0–9 (10 characters).
- symbols
- boolean
- true
- Include `~!@#$%^&*()_+-=[]{};:,./<>?` — the same 27 as the slider on the home page, no more.
- exclude_ambiguous
- boolean
- false
- Drops `0 O 1 I l | o`. Six of them in practice, not seven: `|` is not in the symbol set to begin with. That is why `pool` reads 83 above instead of 89.
- no_repeats
- boolean
- false
- Avoids adjacent repeated characters. Not an absolute guarantee: it retries ten times, exactly as the web generator does. At length 64 that lets a repeat through about 0.1% of the time.
- lang
- string
- `en`
- Which language to answer in. One of the site's eighteen. Also accepted as `?lang=` in the URL, which wins over this field; without either, `Accept-Language` is read. An unknown value is not an error — it falls back to English. See the section below: messages exist in English and Spanish, links in all eighteen.

## Qué significa cada número

Lo mismo: la referencia en inglés, la explicación al lado. Ninguno de estos números es nuevo — todos salen del mismo motor que pinta el medidor de la portada.

- Field
- Notes
- passwords
- An array of strings, always — including with `count` 1.
- analysis.length
- How many characters came back.
- analysis.pool
- The size of the alphabet the password was drawn from.
- analysis.bits
- `H = L·log2(N)`, the same formula as the home page. With `no_repeats` it becomes `log2(N)+(L-1)·log2(N-1)`.
- analysis.log10_guesses
- The expected work, as a base-10 logarithm: half the keyspace.
- analysis.crack_time_log10_seconds
- At 10^12 guesses/s, offline, fast hash. The same attack model as the rest of the site.
- analysis.crack_time
- The same figure in words. `unit` follows the answer language: `years`, `años`…
- analysis.level
- 0–4. The same scale as the checker, ever since the site unified the two it used to have.
- analysis.level_scale
- `"time"`. Says where the level came from, so that a future divergence is visible instead of having to be inferred by comparing numbers.
- analysis.ceiling
- `false`: the server generated the password, so the figure is exact and not a ceiling. The same flag the checker uses.
- notice
- The antipattern warning, in every single response.
- _meta.plan
- `"anonymous"`. The only lane there is; the others arrive with accounts.
- _meta.lang
- `{ "messages", "links" }` — which language each half actually came back in. They can differ, and that is why the API says so instead of leaving you to guess.
- _meta.limits.burst
- The rate limit actually enforced: `limit` requests per `window_seconds`.
- _meta.quota
- The reserved daily-quota slot. `limit` and `remaining` are `null` because nobody counts daily requests yet.
- _meta.docs
- A link back to the documentation.

## El idioma de la respuesta

Por defecto responde en inglés, que es lo que espera quien integra sin decir nada. Se cambia de tres formas, y si coinciden gana la primera: `?lang=` en la URL, `"lang"` en el cuerpo, y la cabecera `Accept-Language`.

Aquí hay una asimetría que conviene saber: **los mensajes existen en inglés y español; los enlaces, en los dieciocho idiomas del sitio.** Pedir alemán te da los enlaces en alemán y los mensajes todavía en inglés.

La petición

```
curl -X POST 'https://api.password.es/v1/generate?lang=de' \
  -H 'content-type: application/json' \
  -d '{"length":20}'
```

La respuesta

```
"_meta": {
  "lang": { "messages": "en", "links": "de" }
}
```

No hay que adivinarlo: cada respuesta declara en `_meta.lang` qué se aplicó a cada mitad. Y un idioma que no existe no es un error — cae a inglés, y `_meta.lang` lo dice.

## Comprobar una contraseña: todavía no

`/v1/check` devuelve `501`. No es un fallo ni un despiste: está así a propósito, y la respuesta explica qué falta y adónde ir mientras tanto. El `checker_url` apunta al comprobador del sitio en el idioma que hayas pedido.

La petición

```
curl -X POST https://api.password.es/v1/check \
  -H 'content-type: application/json' \
  -d '{"password":"x"}'
```

La respuesta

```
{
  "error": "not_implemented",
  "message": "/v1/check does not exist yet. Returning the same numbers as the password.es checker requires its very same pattern engine, and that costs between 11 ms and 3.6 s of CPU per request depending on the input: it is waiting on a plan cap decision and on a length cap. Meanwhile the web checker does exactly this in your browser, sending nothing: https://password.es/en/checker/",
  "checker_url": "https://password.es/en/checker/",
  "docs": "https://password.es/api/",
  "_meta": { "…": "igual que arriba" }
}
```

## Los errores

Todos tienen la misma forma: un `error` corto para el código, un `message` en prosa —que es lo que un asistente de IA le lee a su usuario—, a veces el `field` que lo provocó, un `docs` y el mismo `_meta` de siempre.

- HTTP
- error
- Notes
- 400
- `invalid_length`
- `length` outside 4–64. `field` names it.
- 400
- `invalid_count`
- `count` outside 1–20. `field` names it.
- 400
- `empty_alphabet`
- All four character types turned off, so there is no alphabet to draw from. No `field`: it is the combination, not one parameter.
- 400
- `unknown_parameter`
- A parameter this endpoint does not accept. `field` gives the offending name.
- 429
- `rate_limited`
- Over 60 requests in a minute. Carries `Retry-After` and `RateLimit-*` headers, and `limit` / `window_seconds` in the body.
- 501
- `not_implemented`
- Only from `/v1/check`. Carries `checker_url`, pointing at the web checker in the answer language.

## Los límites

Uno solo, 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 en cada respuesta, dentro de `_meta.limits.burst`.

Al pasarse, la respuesta es un `429` con `Retry-After` y cabeceras `RateLimit-*`, más un mensaje en prosa que dice qué hacer. No lleva enlace a registro ni a precios, porque no hay registro ni precios.

En `_meta` verás además un bloque `quota` con los dos valores en `null`. Está así a propósito: es el hueco reservado para cuando existan las cuentas, y va vacío porque hoy nadie cuenta peticiones por día. Un límite anunciado y no hecho cumplir es peor que no anunciar ninguno.

## El servidor MCP

MCP es el protocolo con el que asistentes como Claude o ChatGPT usan herramientas externas. Conecta esta dirección a tu asistente y podrá generar contraseñas con estos mismos números en vez de inventárselas. Sin registro y sin clave, y con el mismo límite de 60 peticiones por minuto.

Es un servidor **sin estado**, y conviene saberlo si vienes de otros servidores MCP: el POST se responde con JSON y no se abre ningún stream, no se emite ni se espera `Mcp-Session-Id`, una notificación se responde con `202` y sin cuerpo, y el GET devuelve `405`. La especificación 2025-06-18 lo permite explícitamente.

```
curl -X POST https://api.password.es/mcp \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
       "params":{"name":"generate_password",
                 "arguments":{"length":20,"lang":"es"}}}'
```

Publica **una sola herramienta**, `generate_password`, con los mismos parámetros de la tabla de arriba más `lang`. No hay `check_password_strength` y no la habrá mientras `/v1/check` no exista: una herramienta que siempre devuelve error no es una herramienta, es una promesa rota dentro del catálogo de un asistente.

El aviso de que esto es un antipatrón viaja en la descripción de la herramienta y en cada resultado. Es deliberado: es lo que el asistente acaba leyéndole a quien pidió la contraseña.

## La documentación que leen las máquinas

Además de esta página hay una descripción en **OpenAPI 3.1**, y esa sí está publicada: [api.password.es/openapi.json](https://api.password.es/openapi.json). Es lo que lee un generador de clientes, un editor con autocompletado o un agente que quiera saber qué campos existen sin que se los cuente nadie. Describe los mismos parámetros de la tabla de arriba, los códigos de error y el porqué del `quota` vacío.
