API

Dokumentacja API haseł

Dokumentacja api.password.es: każdy parametr, każde pole odpowiedzi, każdy kod błędu i limity. Przykłady wkleja się do terminala tak, jak stoją.

Jeśli szukasz tego, czym to jest, dla kogo i kiedy lepiej go nie używać, zacznij od strony API.

Endpointy

Wszystko wisi pod https://api.password.es. Dwa odpowiadają, a jeden jeszcze nie — i ten mówi to we własnej odpowiedzi.

Tworzenie hasła

Ciało jest opcjonalne: bez niego wychodzi 16 znaków ze wszystkimi czterema rodzajami. Z nim — to, o co poprosisz.

Żądanie
curl -X POST https://api.password.es/v1/generate \
  -H 'content-type: application/json' \
  -d '{"length":20,"exclude_ambiguous":true}'
Odpowiedź
{
  "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/"
  }
}

Parametry

Wszystkie opcjonalne. Tabela jest po angielsku i identyczna we wszystkich osiemnastu językach, celowo: kto wpina API, wpisuje nazwy pól dokładnie tak, jak się je pisze, a osiemnaście tłumaczeń exclude_ambiguous byłoby długiem, nie zakresem.

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.

Co znaczy każda liczba

Ta sama zasada: referencja po angielsku, wyjaśnienie obok. Żadna z tych liczb nie jest nowa — wszystkie wychodzą z tego samego silnika, który rysuje wskaźnik na stronie głównej.

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.

Język odpowiedzi

Domyślnie odpowiada po angielsku, bo tego oczekuje ktoś, kto wpina API bez słowa. Zmienia się to na trzy sposoby, a przy rozbieżności wygrywa pierwszy: ?lang= w URL, "lang" w ciele i nagłówek Accept-Language.

Jest tu asymetria, o której warto wiedzieć: komunikaty istnieją po angielsku i hiszpańsku; odnośniki — we wszystkich osiemnastu językach serwisu. Poproszenie o niemiecki daje niemieckie odnośniki i komunikaty wciąż po angielsku.

Żądanie
curl -X POST 'https://api.password.es/v1/generate?lang=de' \
  -H 'content-type: application/json' \
  -d '{"length":20}'
Odpowiedź
"_meta": {
  "lang": { "messages": "en", "links": "de" }
}

Nie trzeba zgadywać: każda odpowiedź deklaruje w _meta.lang, co zastosowano do której połowy. A język, którego nie ma, nie jest błędem — spada do angielskiego, i _meta.lang to mówi.

Sprawdzanie hasła: jeszcze nie

/v1/check zwraca 501. To nie błąd ani przeoczenie: tak ma być, a odpowiedź tłumaczy, czego brakuje i gdzie iść w międzyczasie. checker_url wskazuje na sprawdzanie na stronie w języku, o który poprosiłeś.

Żądanie
curl -X POST https://api.password.es/v1/check \
  -H 'content-type: application/json' \
  -d '{"password":"x"}'
Odpowiedź
{
  "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" }
}

Błędy

Wszystkie mają ten sam kształt: krótki error jako kod, message zwykłym językiem —to właśnie czyta asystent AI swojemu użytkownikowi—, czasem field, które go wywołało, docs i to samo _meta co zawsze.

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.

Limity

Jeden, i to ten, który naprawdę jest egzekwowany: 60 żądań na minutę na adres IP. Nie trzeba wierzyć tej stronie: liczba podróżuje w każdej odpowiedzi, w _meta.limits.burst.

Po przekroczeniu odpowiedzią jest 429 z Retry-After i nagłówkami RateLimit-* oraz komunikat zwykłym językiem, co robić. Nie ma w nim odnośnika do rejestracji ani do cennika, bo nie ma rejestracji ani cennika.

W _meta zobaczysz też blok quota z obiema wartościami na null. Tak jest celowo: to miejsce zarezerwowane na czas, gdy pojawią się konta, i jest puste, bo dziś nikt nie liczy żądań na dobę. Limit ogłoszony i nieegzekwowany jest gorszy niż nieogłoszenie żadnego.

Serwer MCP

MCP to protokół, którym asystenci tacy jak Claude czy ChatGPT korzystają z zewnętrznych narzędzi. Podłącz ten adres do swojego asystenta, a będzie generował hasła z dokładnie tymi liczbami, zamiast je wymyślać. Bez rejestracji i bez klucza, z tym samym limitem 60 żądań na minutę.

To serwer bezstanowy, i warto o tym wiedzieć, jeśli przychodzisz z innych serwerów MCP: na POST odpowiada JSON-em i nie otwiera strumienia, Mcp-Session-Id nie jest wysyłany ani oczekiwany, powiadomienie dostaje 202 bez treści, a GET zwraca 405. Specyfikacja 2025-06-18 wprost na to pozwala.

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"}}}'

Publikuje jedno narzędzie, generate_password, z tymi samymi parametrami co tabela powyżej plus lang. Nie ma check_password_strength i nie będzie, dopóki nie istnieje /v1/check: narzędzie, które zawsze zwraca błąd, nie jest narzędziem — to złamana obietnica w katalogu asystenta.

Ostrzeżenie, że to antywzorzec, podróżuje w opisie narzędzia i w każdym wyniku. To celowe: to właśnie asystent odczyta osobie, która poprosiła o hasło.

Dokumentacja, którą czytają maszyny

Poza tą stroną istnieje opis w OpenAPI 3.1, i ten jest opublikowany: api.password.es/openapi.json. To właśnie czyta generator klientów, edytor z podpowiadaniem albo agent, który chce się dowiedzieć, jakie pola istnieją, bez pytania kogokolwiek. Opisuje te same parametry co tabela powyżej, kody błędów i powód pustego quota.