Docs

Integrate in three steps.

Base URL https://api.knownpass.com · API v1 · updated 6 September 2026

The whole integration: hash the password on your server with the knownPass salt, send the first six characters of the hash, compare the returned hashes locally. The raw password and its full hash never leave your infrastructure. One GET request, one JSON response, about 100 ms.

1. Hash locally

Compute the SHA-1 of the fixed salt string followed by the password, as lowercase hex. The first six characters are the prefix you send. The remaining 34 are the suffix you keep.

// any runtime with SHA-1
const hash   = sha1("Salted for knownpass.com: " + password).toLowerCase();
const prefix = hash.slice(0, 6);   // sent
const suffix = hash.slice(6);      // kept

// "password123"
// hash   = e4ed298fced32b284fb61840470dd5f9e030b568
// prefix = e4ed29                              (sent)
// suffix = 8fced32b284fb61840470dd5f9e030b568  (kept, 34 chars)

SHA-1 is a lookup key here, not a password-storage hash. The salt is public and fixed on purpose: it keeps knownPass prefixes from being replayed against other services. Details in the threat model.

2. Send the prefix

GET https://api.knownpass.com/v1/range/{prefix}
x-api-key: kpp_…
PartValue
{prefix}Exactly 6 lowercase hex characters. Anything else returns 422.
x-api-keyYour API key. Get one from the console; no card. Missing or invalid returns 401.
$ curl -H "x-api-key: $KP_API_KEY" https://api.knownpass.com/v1/range/e4ed29

3. Compare locally

The response is JSON. prefix echoes what you sent; results maps every known hash in that bucket to the categories it was found in. Keys are full 40-character lowercase hashes, about 1,200 of them for a typical prefix.

{
  "prefix": "5baa61",
  "results": {
    "5baa6100376b63d48b4c8f80a29d72dd7c1da223": ["Brute-force space"],
    "5baa6100eab64a319ee7968afca44f70c579e13d": ["Website leaks", "Password lists"],
    …
  }
}

If the hash you computed in step 1 is a key in results, the password is known: block it at signup and password change, require a change at login. If it isn’t, proceed. The response carries no counts and no plaintexts.

const known = Object.hasOwn(body.results, hash);   // hash: the 40-char value from step 1

Categories you may see: Website leaks, Malware leaks, Password lists, Wordlists, Common, Masks & patterns, Brute-force space, Tailored. Treat any match as a block; the categories are for your logs and your users’ error message.

Response codes

CodeMeaningWhat to do
200JSON {prefix, results} for the prefix. An empty results object is valid: nothing known in that bucket.Compare locally.
422Prefix fails validation: not exactly 6 lowercase hex characters. Body is {"detail": [...]}.Fix the client: length and hex case.
401Missing or invalid x-api-key.Check the key. Rotate it in the console if it leaked.
429Quota exceeded. Retry-After gives the wait in seconds.Fail open, back off, consider Team.
5xxOur fault.Fail open. Check the status page.

Rate limits

Existing keys get 90 days’ notice before any limit change affects them. Pricing.

Fail open

knownPass is a check, not a dependency. If the request fails, times out, or returns 429 or 5xx: let the flow continue, log that the check was skipped, and re-check at the next login or password change. Suggested client timeout: 800 ms. There is no uptime SLA today; the status page is updated by hand.

Where to put the check

The reference is NIST SP 800-63B, section 5.1.1.2: screen against known-compromised and context-specific values; once screening is in place, drop composition rules and periodic rotation.

Machine-readable

Identity providers

Today: plain REST from any stack, as above. Planned: Keycloak, Zitadel, Ory, Authentik, Nextcloud and Supabase Auth. Design partners decide the order. Integrations · Become a design partner.