API

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.

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.

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.

FieldTypeDefaultNotes
lengthinteger 4–6416How many characters. The same range as the generator on this site.
countinteger 1–201How many passwords to return. passwords is always an array, including with count 1.
lowerbooleantrueInclude a–z (26 characters).
upperbooleantrueInclude A–Z (26 characters).
digitsbooleantrueInclude 0–9 (10 characters).
symbolsbooleantrueInclude ~!@#$%^&*()_+-=[]{};:,./<>? — the same 27 as the slider on the home page, no more.
exclude_ambiguousbooleanfalseDrops 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_repeatsbooleanfalseAvoids 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.
langstringenWhich 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.

FieldNotes
passwordsAn array of strings, always — including with count 1.
analysis.lengthHow many characters came back.
analysis.poolThe size of the alphabet the password was drawn from.
analysis.bitsH = L·log2(N), the same formula as the home page. With no_repeats it becomes log2(N)+(L-1)·log2(N-1).
analysis.log10_guessesThe expected work, as a base-10 logarithm: half the keyspace.
analysis.crack_time_log10_secondsAt 1012 guesses/s, offline, fast hash. The same attack model as the rest of the site.
analysis.crack_timeThe same figure in words. unit follows the answer language: years, años
analysis.level0–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.ceilingfalse: the server generated the password, so the figure is exact and not a ceiling. The same flag the checker uses.
noticeThe 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.burstThe rate limit actually enforced: limit requests per window_seconds.
_meta.quotaThe reserved daily-quota slot. limit and remaining are null because nobody counts daily requests yet.
_meta.docsA 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.

HTTPerrorNotes
400invalid_lengthlength outside 4–64. field names it.
400invalid_countcount outside 1–20. field names it.
400empty_alphabetAll four character types turned off, so there is no alphabet to draw from. No field: it is the combination, not one parameter.
400unknown_parameterA parameter this endpoint does not accept. field gives the offending name.
429rate_limitedOver 60 requests in a minute. Carries Retry-After and RateLimit-* headers, and limit / window_seconds in the body.
501not_implementedOnly 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. 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.