Les endpoints
Tout dépend de https://api.password.es. Deux répondent et un pas encore — et celui-là le dit dans sa propre réponse.
- POST/v1/generate Génère un ou plusieurs mots de passe et renvoie leur analyse.
- GET/openapi.json La description de l'API, en OpenAPI 3.1.
-
POST/mcp
Serveur MCP pour assistants. Un outil :
generate_password. - POST/v1/check 501 N'existe pas encore. L'erreur dit ce qui manque et où aller en attendant.
Générer un mot de passe
Le corps est facultatif : sans lui, vous obtenez 16 caractères avec les quatre types activés. Avec lui, ce que vous demandez.
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/"
}
}
Les paramètres
Tous facultatifs. Le tableau est en anglais et identique dans les dix-huit langues, à dessein : celui qui intègre une API tape les noms de champ tels qu'ils s'écrivent, et dix-huit traductions d'exclude_ambiguous seraient une dette, pas une portée.
| 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. |
Ce que veut dire chaque chiffre
Même principe : la référence en anglais, l'explication à côté. Aucun de ces chiffres n'est nouveau — ils sortent tous du même moteur qui dessine la jauge de la page d'accueil.
| 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. |
La langue de la réponse
Par défaut elle répond en anglais, ce qu'attend quelqu'un qui intègre sans rien préciser. Trois façons d'en changer, et en cas de désaccord la première l'emporte : ?lang= dans l'URL, "lang" dans le corps, et l'en-tête Accept-Language.
Il y a ici une asymétrie qu'il vaut mieux connaître : les messages existent en anglais et en espagnol ; les liens, dans les dix-huit langues du site. Demander l'allemand donne des liens en allemand et des messages toujours en anglais.
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" }
}
Pas besoin de deviner : chaque réponse déclare dans _meta.lang ce qui a été appliqué à chaque moitié. Et une langue qui n'existe pas n'est pas une erreur — elle retombe sur l'anglais, et _meta.lang le dit.
Vérifier un mot de passe : pas encore
/v1/check renvoie 501. Ce n'est ni un bug ni un oubli : c'est voulu, et la réponse explique ce qui manque et où aller en attendant. checker_url pointe vers le vérificateur du site dans la langue demandée.
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" }
}
Les erreurs
Elles ont toutes la même forme : un error court pour le code, un message en clair —c'est ce qu'un assistant d'IA lit à son utilisateur—, parfois le field qui l'a provoqué, un docs et le même _meta que d'habitude.
| 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. |
Les limites
Une seule, et c'est celle qui s'applique vraiment : 60 requêtes par minute et par IP. Pas besoin de croire cette page : le nombre voyage dans chaque réponse, dans _meta.limits.burst.
En cas de dépassement, la réponse est un 429 avec Retry-After et des en-têtes RateLimit-*, plus un message en clair qui dit quoi faire. Il ne contient aucun lien vers une inscription ou des tarifs, parce qu'il n'y a ni inscription ni tarifs.
Dans _meta, on trouve aussi un bloc quota dont les deux valeurs sont à null. C'est voulu : c'est la place réservée pour le jour où les comptes existeront, et elle est vide parce qu'aujourd'hui personne ne compte les requêtes par jour. Une limite annoncée et non appliquée est pire que pas de limite annoncée du tout.
Le serveur MCP
MCP est le protocole avec lequel des assistants comme Claude ou ChatGPT utilisent des outils externes. Connectez cette adresse à votre assistant et il générera des mots de passe avec ces mêmes chiffres au lieu de les inventer. Sans inscription et sans clé, avec la même limite de 60 requêtes par minute.
C'est un serveur sans état, et il vaut mieux le savoir si vous venez d'autres serveurs MCP : le POST reçoit une réponse JSON sans ouvrir de flux, Mcp-Session-Id n'est ni émis ni attendu, une notification reçoit 202 sans corps, et le GET renvoie 405. La spécification 2025-06-18 l'autorise explicitement.
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"}}}'
Il publie un seul outil, generate_password, avec les mêmes paramètres que le tableau ci-dessus plus lang. Il n'y a pas de check_password_strength et il n'y en aura pas tant que /v1/check n'existera pas : un outil qui renvoie toujours une erreur n'est pas un outil, c'est une promesse rompue dans le catalogue d'un assistant.
L'avertissement disant que c'est un antipatron voyage dans la description de l'outil et dans chaque résultat. C'est délibéré : c'est ce que l'assistant finit par lire à celui qui a demandé le mot de passe.
La documentation que lisent les machines
En plus de cette page, il existe une description en OpenAPI 3.1, et celle-là est bien publiée : api.password.es/openapi.json. C'est ce que lit un générateur de clients, un éditeur avec autocomplétion ou un agent qui veut savoir quels champs existent sans que personne le lui dise. Elle décrit les mêmes paramètres que le tableau ci-dessus, les codes d'erreur et la raison du quota vide.