Die Endpunkte
Alles hängt an https://api.password.es. Zwei antworten und einer noch nicht — und der eine sagt es in seiner eigenen Antwort.
- POST/v1/generate Erzeugt ein oder mehrere Passwörter und gibt ihre Analyse zurück.
- GET/openapi.json Die Beschreibung der API, in OpenAPI 3.1.
-
POST/mcp
MCP-Server für Assistenten. Ein Werkzeug:
generate_password. - POST/v1/check 501 Gibt es noch nicht. Der Fehler sagt, was fehlt und wohin man solange geht.
Ein Passwort erzeugen
Der Body ist optional: ohne ihn bekommst du 16 Zeichen mit allen vier Zeichenarten. Mit ihm das, was du verlangst.
curl -X POST https://api.password.es/v1/generate \
-H 'content-type: application/json' \
-d '{"length":20,"exclude_ambiguous":true}'
{
"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/"
}
}
Die Parameter
Alle optional. Die Tabelle steht auf Englisch und ist in allen achtzehn Sprachen identisch, mit Absicht: wer eine API einbindet, tippt die Feldnamen genau so, wie sie geschrieben sind, und achtzehn Übersetzungen von exclude_ambiguous wären Schuld, nicht Umfang.
| 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. |
Was jede Zahl bedeutet
Dasselbe: die Referenz auf Englisch, die Erklärung daneben. Keine dieser Zahlen ist neu — sie kommen alle aus demselben Motor, der auf der Startseite die Anzeige zeichnet.
| 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 1012 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. |
Die Sprache der Antwort
Standardmäßig antwortet sie auf Englisch — das erwartet, wer ohne weitere Angabe einbindet. Drei Wege, das zu ändern, und bei Widerspruch gewinnt der erste: ?lang= in der URL, "lang" im Body und der Header Accept-Language.
Hier gibt es eine Schieflage, die man kennen sollte: die Meldungen gibt es auf Englisch und Spanisch; die Links in allen achtzehn Sprachen der Website. Wer Deutsch verlangt, bekommt deutsche Links und Meldungen weiterhin auf Englisch.
curl -X POST 'https://api.password.es/v1/generate?lang=de' \
-H 'content-type: application/json' \
-d '{"length":20}'
"_meta": {
"lang": { "messages": "en", "links": "de" }
}
Raten muss man nicht: jede Antwort erklärt in _meta.lang, was auf welche Hälfte angewandt wurde. Und eine Sprache, die es nicht gibt, ist kein Fehler — sie fällt auf Englisch zurück, und _meta.lang sagt es.
Ein Passwort prüfen: noch nicht
/v1/check gibt 501 zurück. Weder Bug noch Versehen: es ist Absicht, und die Antwort erklärt, was fehlt und wohin man solange geht. checker_url zeigt auf den Checker der Website in der angeforderten Sprache.
curl -X POST https://api.password.es/v1/check \
-H 'content-type: application/json' \
-d '{"password":"x"}'
{
"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" }
}
Die Fehler
Alle haben dieselbe Form: ein kurzer error als Code, ein message in Klartext —das ist es, was ein KI-Assistent seinem Nutzer vorliest—, manchmal das field, das ihn ausgelöst hat, ein docs und dasselbe _meta wie immer.
| 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. |
Die Grenzen
Eine, und es ist die, die wirklich durchgesetzt wird: 60 Anfragen pro Minute und IP. Man muss dieser Seite nicht glauben: die Zahl reist in jeder Antwort mit, in _meta.limits.burst.
Wer darüber hinausgeht, bekommt einen 429 mit Retry-After und RateLimit-*-Headern, dazu eine Nachricht in Klartext, die sagt, was zu tun ist. Sie enthält keinen Link auf Registrierung oder Preise, weil es weder Registrierung noch Preise gibt.
In _meta steht außerdem ein Block quota, beide Werte auf null. Das ist Absicht: der reservierte Platz für den Tag, an dem es Konten gibt — leer, weil heute niemand Anfragen pro Tag zählt. Eine angekündigte und nicht durchgesetzte Grenze ist schlimmer, als gar keine anzukündigen.
Der MCP-Server
MCP ist das Protokoll, mit dem Assistenten wie Claude oder ChatGPT externe Werkzeuge nutzen. Verbinde diese Adresse mit deinem Assistenten, und er erzeugt Passwörter mit genau diesen Zahlen, statt sie zu erfinden. Ohne Registrierung, ohne Schlüssel und mit demselben Limit von 60 Anfragen pro Minute.
Es ist ein zustandsloser Server, und das ist wichtig zu wissen, wenn du von anderen MCP-Servern kommst: Der POST wird mit JSON beantwortet, ohne Stream, Mcp-Session-Id wird weder gesendet noch erwartet, eine Notification erhält 202 ohne Body, und der GET liefert 405. Die Spezifikation 2025-06-18 erlaubt das ausdrücklich.
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"}}}'
Er veröffentlicht ein einziges Werkzeug, generate_password, mit denselben Parametern wie in der Tabelle oben plus lang. Es gibt kein check_password_strength und wird keines geben, solange /v1/check nicht existiert: Ein Werkzeug, das immer einen Fehler zurückgibt, ist kein Werkzeug, sondern ein gebrochenes Versprechen im Katalog eines Assistenten.
Der Hinweis, dass dies ein Antipattern ist, reist in der Werkzeugbeschreibung und in jedem Ergebnis mit. Das ist Absicht: Es ist das, was der Assistent demjenigen vorliest, der nach dem Passwort gefragt hat.
Die Dokumentation, die Maschinen lesen
Neben dieser Seite gibt es eine OpenAPI-3.1-Beschreibung, und die ist veröffentlicht: api.password.es/openapi.json. Sie ist das, was ein Client-Generator, ein Editor mit Autovervollständigung oder ein Agent liest, um zu erfahren, welche Felder es gibt, ohne dass es ihm jemand sagt. Sie beschreibt dieselben Parameter wie die Tabelle oben, die Fehlercodes und den Grund für das leere quota.