API reference
Everything you need to add real strength scoring, breach screening and
NIST-aligned policy validation to your signup and reset flows. Every
endpoint is a POST request that exchanges
JSON — no SDK required.
What the API does
Passwordify's REST API brings the same checks that power the free browser tools to your backend: real strength scoring from zxcvbn, breach screening against the Have I Been Pwned corpus (900M+ passwords) via k-anonymity, policy validation aligned with NIST 800-63B, and password or passphrase generation from a cryptographically secure random source (CSPRNG). Drop it into a signup form, a password reset flow, or an internal admin tool.
Base URL
https://www.passwordify.xyz/api
Format
Every endpoint is a POST. Send and
receive application/json.
Authentication
Every request needs an API key, sent in the
X-API-Key header:
X-API-Key: pk_live_...
Don't have a key yet? Every example on this page also works with the
free, rate-limited demo key
pk_test_passwordify_demo — no signup
required. Create a production key on the
Developers
page when you're ready to go live.
Example
# Try it immediately with the free demo key (rate-limited)
curl https://www.passwordify.xyz/api/v1/strength \
-H "X-API-Key: pk_test_passwordify_demo" \
-H "Content-Type: application/json" \
-d '{"password":"Tr0ub4dor92!"}'
# -> 200 OK
{
"score": 3,
"guesses": 1834729102,
"entropyBits": 46.8,
"crackTime": "3 months",
"feedback": { "warning": "", "suggestions": [] }
}
Keys are secrets. Call
Passwordify from your backend only. Never ship a
pk_live_ key inside client-side
JavaScript, a mobile app binary, or a public repository — the demo
key above is the only one safe to expose.
Rate limits
Limits apply on a rolling monthly window tied to your plan. Need more headroom? See pricing.
| Tier | Requests / month | Notes |
|---|---|---|
| Free | 1,000 | 1 key, community support |
| Pro | 100,000 | 5 keys, email support |
| Scale | 1,000,000 | Unlimited keys, custom limits, SLA |
Every response includes your current usage:
-
X-RateLimit-Limittotal requests allowed in the current window -
X-RateLimit-Remainingrequests left before you're throttled -
X-RateLimit-ResetUnix timestamp when the window resets
Example — exceeding your quota
# Exceeding your plan's monthly quota
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1718900000
{
"error": {
"code": "rate_limited",
"message": "Monthly request limit exceeded. Upgrade your plan or wait for the next billing cycle."
}
}
Errors
Failed requests return a non-2xx HTTP status and a JSON body with the same shape:
{
"error": {
"code": "bad_request",
"message": "Field 'password' is required."
}
}
| Status | Code | Meaning |
|---|---|---|
| 400 | bad_request | Missing or malformed request body |
| 401 | unauthorized | Missing, invalid, or revoked API key |
| 429 | rate_limited | Monthly or burst rate limit exceeded |
| 500 | internal_error | Unexpected server error — safe to retry with backoff |
Endpoints
Four endpoints cover strength, breach, policy validation, and
generation. Every one is a POST with a
JSON body and a JSON response.
/v1/strength
Score a password's real-world strength with zxcvbn's pattern, dictionary, and keyboard-walk matching — far more accurate than a length-and-character-class regex meter.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| password | string | Yes | Plaintext password to score. Never logged or persisted. |
| userInputs | string[] | No | Context words (username, email, site name) that count against the score if reused in the password. |
Example
# Score a password as the user types
curl https://www.passwordify.xyz/api/v1/strength \
-H "X-API-Key: pk_live_..." \
-H "Content-Type: application/json" \
-d '{"password":"Tr0ub4dor92!","userInputs":["emanuele","passwordify"]}'
# -> 200 OK
{
"score": 3,
"guesses": 1834729102,
"entropyBits": 46.8,
"crackTime": "3 months",
"feedback": {
"warning": "",
"suggestions": ["Add another word or two. Uncommon words are safer than substitutions."]
}
}
/v1/breach
Check whether a password appears in the Have I Been Pwned corpus using k-anonymity — only a 5-character SHA-1 prefix ever leaves the caller, never the password itself.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| password | string | One of password / sha1Prefix | Plaintext password to check. Hashed and truncated to a 5-character prefix before comparison. |
| sha1Prefix | string | One of password / sha1Prefix | Precomputed 5-character SHA-1 prefix, for callers that hash client-side. |
Example
# "5BAA6" is the SHA-1 prefix of the word "password" — always breached
curl https://www.passwordify.xyz/api/v1/breach \
-H "X-API-Key: pk_live_..." \
-H "Content-Type: application/json" \
-d '{"sha1Prefix":"5BAA6"}'
# -> 200 OK
{
"breached": true,
"count": 9545824
}
/v1/validate
Combine strength scoring and breach screening against a configurable NIST 800-63B policy in one call — the endpoint most signup and reset flows use directly.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| password | string | Yes | Plaintext password to validate. |
| policy | object | No |
Overrides for minLength (default 8),
maxLength (default 64),
minScore (default 2, 0–4), and
blockBreached (default true).
|
Example
# Screen a password at signup — strength + breach + policy
curl https://www.passwordify.xyz/api/v1/validate \
-H "X-API-Key: pk_live_..." \
-H "Content-Type: application/json" \
-d '{"password":"Summer2024!","policy":{"minLength":12,"minScore":3}}'
# -> 200 OK
{
"valid": false,
"score": 2,
"breached": true,
"breachCount": 3529,
"violations": ["min_length", "found_in_breach"]
}
/v1/generate
Generate cryptographically strong passwords or diceware-style passphrases from a CSPRNG — nothing pseudo-random, nothing seeded from a timestamp.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| mode | "password" | "passphrase" | No | Generation mode. Default "password". |
| length | number | No | Character count in password mode (8–128). Default 20. |
| count | number | No | Number of results to return. Default 1, max 50. |
| lowercase | boolean | No | Include lowercase letters. Default true. |
| uppercase | boolean | No | Include uppercase letters. Default true. |
| numbers | boolean | No | Include digits. Default true. |
| symbols | boolean | No | Include symbols. Default true. |
| avoidAmbiguous | boolean | No | Exclude visually ambiguous characters (l, 1, I, O, 0). Default false. |
| words | number | No | Word count in passphrase mode. Default 5 (~64.5 bits at 12.9 bits/word). |
| separator | string | No | Separator between words in passphrase mode. Default "-". |
Example
# A memorable diceware passphrase, two variants
curl https://www.passwordify.xyz/api/v1/generate \
-H "X-API-Key: pk_live_..." \
-H "Content-Type: application/json" \
-d '{"mode":"passphrase","words":5,"separator":"-","count":2}'
# -> 200 OK
{
"passwords": ["glacier-anchor-mural-triangle-woven", "pebble-orchard-drift-lantern-cove"],
"entropyBits": 64.5
}
Code examples
The same /v1/validate call, in four languages.
curl https://www.passwordify.xyz/api/v1/validate \
-H "X-API-Key: pk_live_..." \
-H "Content-Type: application/json" \
-d '{"password":"Summer2024!"}'
const res = await fetch("https://www.passwordify.xyz/api/v1/validate", {
method: "POST",
headers: {
"X-API-Key": "pk_live_...",
"Content-Type": "application/json",
},
body: JSON.stringify({ password: "Summer2024!" }),
});
const data = await res.json();
console.log(data.valid, data.violations);
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "pk_live_...");
var payload = JsonSerializer.Serialize(new { password = "Summer2024!" });
var content = new StringContent(payload, Encoding.UTF8, "application/json");
var response = await client.PostAsync(
"https://www.passwordify.xyz/api/v1/validate", content);
Console.WriteLine(await response.Content.ReadAsStringAsync());
import requests
response = requests.post(
"https://www.passwordify.xyz/api/v1/validate",
headers={"X-API-Key": "pk_live_..."},
json={"password": "Summer2024!"},
)
data = response.json()
print(data["valid"], data["violations"])