Getting started

Bastion is a password audit API. It evaluates passwords and returns strength scores, crack time estimates, entropy, and real breach data.

The base URL for the native API is:

https://bastion.eande171.workers.dev

Free tier access

Native keys are for the free tier. They allow you to use all endpoints with a limit of 100 requests per day.

Your first request

Register for a native key using your email. The email is hashed immediately before storage and is only used if you need to regenerate your key. You can also register here.

const res = await fetch("https://bastion.eande171.workers.dev/v1/keys/register", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: "you@example.com" }) }); const { api_key, regen_token } = await res.json();
curl -X POST https://bastion.eande171.workers.dev/v1/keys/register \ -H "Content-Type: application/json" \ -d '{"email": "you@example.com"}'
Store your api_key and regen_token securely. The regeneration token cannot be recovered after registration.

Then evaluate a password using your new key:

const res = await fetch("https://bastion.eande171.workers.dev/v1/evaluate", { method: "POST", headers: { "Authorization": "Bearer bsn_live_...", "Content-Type": "application/json" }, body: JSON.stringify({ password: "hunter2" }) }); const result = await res.json();
curl -X POST https://bastion.eande171.workers.dev/v1/evaluate \ -H "Authorization: Bearer bsn_live_..." \ -H "Content-Type: application/json" \ -d '{"password": "hunter2"}'

Authentication

Native API keys must be passed in the Authorization header using the Bearer scheme:

Authorization: Bearer bsn_live_...
PrefixPurpose
bsn_live_Live API key - used for authenticated requests
bsn_regen_Regeneration token - used to issue a new key if one is compromised

Endpoint reference

POST /v1/evaluate
๐Ÿ”‘ Requires authentication

Evaluates a password and returns a full audit result including strength score, crack times, entropy, breach status, and feedback.

Query parameters

ParameterTypeDefaultDescription
hibpstringtrueSet to false to skip the HaveIBeenPwned breach check. breached and breach_count will be null in the response.

Request body

{ "password": "hunter2" }

Response

{ "score": 1, "strength": "Weak", "entropy_bits": 12.972441366563533, "crack_times": { "online_throttled": "3 days", "online_unthrottled": "13 minutes", "offline_slow_hash": "less than a second", "offline_fast_hash": "less than a second" }, "breached": true, "breach_count": 65744, "warning": "This is a very common password.", "suggestions": ["Add another word or two. Uncommon words are better."] }
FieldDescription
score0 (Very Weak) to 4 (Very Strong)
strengthHuman-readable label corresponding to the score
entropy_bitsPassword entropy in bits
crack_timesEstimated time to crack under four attack scenarios - throttled online, unthrottled online, slow offline hash, and fast offline hash
breachedWhether the password appears in known breaches. null if hibp=false
breach_countNumber of times seen in known breaches. null if hibp=false
warningA specific weakness warning, or null for stronger passwords
suggestionsImprovement suggestions, or null for stronger passwords
POST /v1/keys/register
No authentication required

Registers a new API key. Each email address can only be registered once. The email is hashed immediately before storage.

Request body

{ "email": "you@example.com" }

Response

{ "api_key": "bsn_live_...", "regen_token": "bsn_regen_..." }
Store both values securely. This regeneration token cannot be recovered after this response.
POST /v1/keys/regenerate
No authentication required

Invalidates the current API key and issues a new one. Useful if a key is compromised. A new regeneration token is also issued.

Request body

{ "email": "you@example.com", "regen_token": "bsn_regen_..." }

Response

{ "api_key": "bsn_live_...", "regen_token": "bsn_regen_..." }
The old key is immediately invalidated. Store the new regeneration token. If it is lost, the key cannot be regenerated.
GET /v1/keys/usage
๐Ÿ”‘ Requires authentication

Returns current usage statistics for the authenticated key. Free tier keys are capped at 100 requests per day.

Response

{ "tier": "Free", "usage": 42, "limit": 100, "hard_limit": null, "reset_at": 1234567890000 }

reset_at is a Unix timestamp in milliseconds. hard_limit is null if no hard cap has been set.

Note: hard_limit is only applicable to paid tiers using native keys (which don't currently exist).
The endpoint to alter hard_limit does exist but is not publicly documented.

Error reference

All errors return a JSON body with an appropriate HTTP status code. Handle them using res.json(). The error field is a stable machine-readable code; the message field is human-readable.

Error CodeStatusMessage
MISSING_AUTH_HEADER401No Authorization header provided.
INVALID_AUTH_HEADER401Authorization header is not in the correct format.
INVALID_API_KEY401Key does not have a valid bsn_live_ prefix.
API_KEY_NOT_FOUND401Key is valid but not registered.
INVALID_EMAIL400Email is not in a valid format.
EMAIL_ALREADY_EXISTS400Email is already registered.
INVALID_REGEN_TOKEN401Provided regeneration token is invalid.
RATE_LIMIT_EXCEEDED429API rate limit exceeded.
DEMO_LIMIT_EXCEEDED429Demo limit reached. Sign up for a free API key.
HARD_LIMIT_EXCEEDED429User-defined hard limit exceeded.
HARD_LIMIT_TOO_LOW400Provided hard limit is below the usage limit.
INVALID_JSON_BODY400Request body is missing or malformed.
PASSWORD_EMPTY400Password cannot be empty.
PASSWORD_TOO_LONG400Password cannot exceed 128 characters.
PAID_TIER_NOT_IMPLEMENTED501Features for paid tiers are not implemented yet.

Code examples

Evaluate a password

const res = await fetch("https://bastion.eande171.workers.dev/v1/evaluate", { method: "POST", headers: { "Authorization": "Bearer bsn_live_...", "Content-Type": "application/json" }, body: JSON.stringify({ password: "correct-horse-battery-staple" }) }); if (!res.ok) { const { error, message } = await res.json(); console.error(error, message); } else { const result = await res.json(); console.log(result); }
curl -X POST https://bastion.eande171.workers.dev/v1/evaluate \ -H "Authorization: Bearer bsn_live_..." \ -H "Content-Type: application/json" \ -d '{"password": "correct-horse-battery-staple"}'

Skip the breach check

const res = await fetch("https://bastion.eande171.workers.dev/v1/evaluate?hibp=false", { method: "POST", headers: { "Authorization": "Bearer bsn_live_...", "Content-Type": "application/json" }, body: JSON.stringify({ password: "correct-horse-battery-staple" }) });
curl -X POST "https://bastion.eande171.workers.dev/v1/evaluate?hibp=false" \ -H "Authorization: Bearer bsn_live_..." \ -H "Content-Type: application/json" \ -d '{"password": "correct-horse-battery-staple"}'

Regenerate a key

const res = await fetch("https://bastion.eande171.workers.dev/v1/keys/regenerate", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: "you@example.com", regen_token: "bsn_regen_..." }) }); const { api_key, regen_token } = await res.json();
curl -X POST https://bastion.eande171.workers.dev/v1/keys/regenerate \ -H "Content-Type: application/json" \ -d '{"email": "you@example.com", "regen_token": "bsn_regen_..."}'

Check usage

const res = await fetch("https://bastion.eande171.workers.dev/v1/keys/usage", { headers: { "Authorization": "Bearer bsn_live_..." } }); const { tier, usage, limit, reset_at } = await res.json(); console.log(`${usage} / ${limit} requests used`);
curl https://bastion.eande171.workers.dev/v1/keys/usage \ -H "Authorization: Bearer bsn_live_..."