Have I Been Pwned

Security API · Password breach check · k-anonymity · Troy Hunt

TL;DR

Have I Been Pwned's Pwned Passwords API lets you check if a password appears in known data breaches — without ever sending the full password. Using a k-anonymity model, you only send the first 5 characters of the SHA-1 hash. The API returns a list of matching hash suffixes; you check locally if your full hash appears. Created and maintained by Troy Hunt. No API key required.

Quick start: https://api.pwnedpasswords.com/range/21BD1

No API key needed — free password safety check!

How to Use This API

1. Check a Password Hash Prefix

Send first 5 chars of SHA-1 hash, get back matching full hashes:

https://api.pwnedpasswords.com/range/21BD1

2. JavaScript — Safe Password Check

Full client-side implementation using k-anonymity — your password never leaves the browser:

async function checkPassword(password) {
  const hash = await crypto.subtle.digest('SHA-1',
    new TextEncoder().encode(password));
  const hex = Array.from(new Uint8Array(hash))
    .map(b => b.toString(16).padStart(2, '0')).join('').toUpperCase();
  const prefix = hex.slice(0, 5);
  const suffix = hex.slice(5);

  const resp = await fetch(
    `https://api.pwnedpasswords.com/range/${prefix}`
  );
  const text = await resp.text();
  const matches = text.split('\n').map(l => l.split(':')[0]);
  return matches.includes(suffix);
}

checkPassword('password123').then(pwned => {
  console.log(pwned ? 'Password has been pwned!' : 'Password is safe');
});

3. Python — Password Validation

import hashlib, requests

def check_password(password):
    hash_hex = hashlib.sha1(password.encode()).hexdigest().upper()
    prefix, suffix = hash_hex[:5], hash_hex[5:]
    resp = requests.get(
        f'https://api.pwnedpasswords.com/range/{prefix}'
    )
    return suffix in [line.split(':')[0]
                      for line in resp.text.splitlines()]

passwords = ['correcthorsebatterystaple', 'password123', 'qwerty']
for pw in passwords:
    pwned = check_password(pw)
    print(f"'{pw}': {'BREACHED' if pwned else 'Safe'}")

4. Adding padding for privacy

Add ?AddPadding=true to get responses with padding (responses are uniform in size):

https://api.pwnedpasswords.com/range/21BD1?AddPadding=true
Hash prefix 21BD1: https://api.pwnedpasswords.com/range/21BD1

Frequently Asked Questions

How does k-anonymity protect my password?
Your password is hashed with SHA-1. Only the first 5 hex characters (20 bits) are sent to the API — this matches at least 380+ other hashes. The API returns all those hash suffixes; you check locally if yours is among them. The API never learns your full hash or password.
How many passwords are in the database?
Over 1.5 billion unique compromised passwords collected from hundreds of data breaches. The database grows continuously as new breaches are added.
Is there a rate limit?
No documented rate limit. The API is designed for high availability and is free. The CORS-enabled endpoint allows direct browser access for client-side integration.
What format does the API return?
Plain text. Each line is a hash suffix and count separated by a colon: 0018A45C4D1DEF81644B54AB7F969B88D65:4. The count indicates how many times that hash appears in breaches.
Can I check email addresses too?
The password check API (range endpoint) is free and open. The email breach check API requires an API key and is for subscription users. The range endpoint is always free.
Should I add padding to my requests?
Use ?AddPadding=true if you want all API responses to be the same size, preventing an observer from inferring how many hashes matched your prefix. For most applications, it's optional.

API Details

API URL
https://api.pwnedpasswords.com/range/{hash-prefix}
Documentation
haveibeenpwned.com/API/v3
Category
Security
Authentication
Not Required (range endpoint is free and open)
Privacy Model
k-anonymity — only first 5 chars of SHA-1 hash sent

What You Can Build