# A API de password.es xa responde: como se usa

> Unha chamada sen clave nin rexistro que devolve contrasinais e os mesmos números que ves na web: bits, tempo de rotura e nivel. Incluído o que aínda non fai, e por que chamala non sempre é boa idea.

2026-08-31 · David Carrero · password.es
Original: https://password.es/gl/blog/api-para-xerar-contrasinais/

---

Este blogue leva meses repetindo o mesmo: [o teu contrasinal non debería saír do
navegador](/gl/blog/por-que-non-enviamos-o-teu-contrasinal/). O
[xerador](/gl/) saca o azar de `crypto.getRandomValues()` na túa propia máquina
e o [comprobador](/gl/comprobador/) analiza o que escribes sen envialo a ningún
sitio.

E hoxe publicamos unha API que xera contrasinais nun servidor.

**A contradición é evidente e non a imos disimular**: cada resposta da API leva
un aviso que di exactamente iso. Pero hai sitios onde non hai navegador —un
script que dá de alta cen contas, un servidor que fabrica unha credencial
temporal, un asistente ao que alguén lle pide un contrasinal e o inventa— e alí
a alternativa non é «o navegador»: é un `random()` mal escollido ou unha cadea
escrita a man. Para iso está isto.

## A chamada enteira

Sen clave, sen rexistro, sen cabeceiras. Isto é todo:

`curl -X POST https://api.password.es/v1/generate`

Devolve un contrasinal de 16 caracteres e a súa análise. Executada mentres se
escribía este parágrafo: `x*7$9,BUy9PPvOI?`, sacada dun alfabeto de 89
caracteres, **103,6 bits** de entropía e un tempo de rotura de 2,5 × 10¹¹ anos.
Nivel 4 sobre 4.

Ningún destes números é novo. Saen do mesmo motor que pinta o medidor da
portada: a mesma fórmula de [bits](/gl/blog/que-son-os-bits-de-entropia/), o
mesmo modelo de ataque —10¹² intentos por segundo, sen conexión, hash rápido— e
a mesma escala de nivel do comprobador. Se a web e a API desen números distintos
para o mesmo contrasinal, un dos dous estaría mentindo.

## Que devolve, campo a campo

A resposta trae `passwords` —sempre un array, aínda que pidas un só— e un bloque
`analysis` con `length`, `pool`, `bits`, `log10_guesses`,
`crack_time_log10_seconds`, `crack_time`, `level`, `level_scale` e `ceiling`.

Dous merecen un parágrafo. **`level_scale` di de onde saíu o nivel** (hoxe
sempre `"time"`), para que unha diverxencia futura entre a web e a API se vexa
no campo en vez de haber que deducila comparando números. E **`ceiling` di se a
cifra é exacta ou un teito**: en `generate` é sempre `false`, porque o
contrasinal fíxoo o servidor e sabe con que alfabeto; no comprobador da web non
sempre pode selo, porque alí o contrasinal tráelo ti.

Despois vén `notice`, o aviso, e un bloque `_meta` co plan, o idioma aplicado,
os límites e unha ligazón á documentación.

## As opcións

Todas opcionais, e cos nomes en inglés porque quen integra unha API escribe os
campos tal cal: `length` (4–64, por defecto 16), `count` (1–20), `lower`,
`upper`, `digits`, `symbols`, `exclude_ambiguous`, `no_repeats` e `lang`.

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

Dous detalles que a documentación conta e que convén saber antes de integralo.
**`exclude_ambiguous` quita seis caracteres, non sete**: descarta
`0 O 1 I l | o`, pero a barra vertical non estaba no xogo de símbolos para
empezar, así que o alfabeto baixa de 89 a 83 e non a 82. Por iso a resposta di
`pool` 83. E **`no_repeats` non é unha garantía absoluta**: reinténtao dez
veces, exactamente como o xerador da web, e a 64 caracteres cólase unha
repetición contigua arredor do 0,1 % das veces. Dicilo é máis útil que prometer
o contrario.

## O idioma

Por defecto responde en inglés, que é o que espera quen integra sen dicir nada.
Cámbiase con `?lang=` no URL, con `"lang"` no corpo ou coa cabeceira
`Accept-Language`, e se se contradín gaña o primeiro. Un idioma que non existe
non é un erro: cae ao inglés.

Aquí hai unha asimetría que non se adiviña, e por iso a API decláraa: **as
mensaxes existen en inglés e castelán; as ligazóns, nos dezaoito idiomas do
sitio**. Pedir alemán dáche as ligazóns en alemán e as mensaxes aínda en inglés.
Non hai que supoñelo: `_meta.lang` trae `{ "messages", "links" }` co que se
aplicou a cada metade.

## O servidor MCP

MCP é o protocolo co que un asistente —Claude, ChatGPT— usa ferramentas
externas. `https://api.password.es/mcp` é un servidor MCP sen rexistro e sen
clave, e publica unha soa ferramenta: `generate_password`, cos mesmos parámetros
de arriba.

É a parte que máis nos importa, e non polo técnico: **un asistente que non ten
de onde sacar un contrasinal invéntao**, e o que sae de aí non é aleatorio, é o
que o modelo considera que ten forma de contrasinal. Co servidor conectado deixa
de improvisar e devolve un feito con `crypto.getRandomValues()`, coa súa análise
e co aviso incluído na descrición da ferramenta e en cada resultado. Ese aviso é
xusto o que o asistente acaba lendo a quen lle pediu o contrasinal, e por iso
está aí.

Se vés doutros servidores MCP, un só dato: **este non ten estado.** O POST
respóndese con JSON e non abre ningún stream, `Mcp-Session-Id` nin se emite nin
se espera, unha notificación respóndese con 202 sen corpo e o GET devolve 405. A
especificación 2025-06-18 permíteo explicitamente, pero quen espere sesións e
SSE vai depurar ás cegas se ninguén llo conta.

## O que non fai

**`/v1/check` devolve 501.** Non é un descoido nin un endpoint a medio facer:
está así a propósito, e a propia resposta explica por que. Devolver os mesmos
números que o comprobador esixe o mesmo motor de patróns que corre na web, e iso
custa entre 11 ms e 3,6 s de CPU por petición segundo o que lle mandes. Nun
servizo sen rexistro e sen clave, esa forquita é unha decisión de produto —onde
se pon o teito de lonxitude— que aínda non está tomada. Mentres tanto, o 501
trae un `checker_url` que apunta ao comprobador da web no idioma que pidas, que
fai exactamente iso sen enviar nada.

Tampouco existen as contas, nin as claves, nin os plans. E como non existen, non
hai unha soa ligazón a un rexistro en toda a API: nin sequera no erro de límite
superado, que é onde todo o mundo a pon.

## Os límites

Un, e é o que se aplica de verdade: **60 peticións por minuto e por IP**. Non fai
falta crer esta páxina: o número viaxa dentro de cada resposta, en
`_meta.limits.burst`. Ao pasarse sae un 429 con `Retry-After` e cabeceiras
`RateLimit-*`.

En `_meta` verás ademais un bloque `quota` cos valores a `null`. É o oco
reservado para cando haxa contas, e vai baleiro a propósito: **un límite
anunciado e non aplicado é peor que non anunciar ningún**, porque quen integra
respéctao e programa contra un número que ninguén está a contar.

## E aínda así, pénsao dúas veces

Unha API que xera contrasinais é, no fondo, un antipatrón: o contrasinal viaxa
pola rede e pasa por unha máquina que non é a túa. Que non gardemos nada non
cambia a forma do problema, só a nosa parte —e xa sabes o que vale [unha promesa
que non podes comprobar](/gl/blog/por-que-non-enviamos-o-teu-contrasinal/).

Por iso o aviso non vive só nesta páxina: viaxa en cada resposta, no idioma que
pidas. E por iso este parágrafo vai aquí e non escondido ao final da
documentación. **Para un contrasinal que vaias usar ti, o [xerador](/gl/) deste
sitio corre enteiro no teu navegador e non envía nada.** A API é para o outro: o
que ocorre sen ninguén diante dunha pantalla.

Os detalles están en dúas páxinas. [Que é a API](/gl/api/) conta para quen é,
que responde hoxe e que non. A [referencia](/gl/api/docs/) ten os nove
parámetros, todos os campos explicados un a un, os códigos de erro e os límites;
é a que se abre co editor ao lado. E se o que queres é que o lea unha máquina,
está o `openapi.json`.

---

*Fontes: a propia API, verificada contra produción o 31 de agosto de 2026 —
`POST /v1/generate`, `POST /v1/check` (501), `POST /mcp` e `GET /openapi.json` ·
os números da análise saen do mesmo motor que o xerador e o comprobador de
password.es · o modelo de ataque é 10¹² intentos por segundo, sen conexión e con
hash rápido, o mesmo do resto do sitio · o servidor MCP implementa a
especificación 2025-06-18 con transporte Streamable HTTP sen estado.*
