All API docs

Pwned passwords

Check whether a password has appeared in a breach without ever sending it. The client hashes locally and only transmits a five-character prefix using k-anonymity. No API key required.

Inject in DI
  • IPwnedPasswordsClientSHA-1 and NTLM range queries that keep the plaintext password on your machine.

Inject IPwnedPasswordsClient. Password checks need no API key, so this surface works with an empty HibpOptions as long as a User-Agent is set.

RegistrationC#
using HaveIBeenPwned.Client;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddPwnedServices(options =>
{
    options.UserAgent = "MyApp/1.0";
});

var app = builder.Build();

GetPwnedPasswordAsyncNone

Hashes the password with SHA-1, sends the first five hash characters, and evaluates the returned range locally. IsPwned is false when no suffix matches.

GETapi.pwnedpasswords.com/range/{hashPrefix}Request detailsHide details
HTTP requestHTTP/1.1
GET /range/{hashPrefix} HTTP/1.1
Host: api.pwnedpasswords.com
Accept: text/plain
AuthenticationNone

This public endpoint is called without an API key or authorization header.

Response bodyPlain-text hash range

The SDK parses the suffix/count rows in memory and returns only the local match.

Official HIBP reference
Try this endpointWatch the same privacy-preserving work the SDK performs inside your process.

The demo mirrors pwned.client: your password stays in this browser tab and only a five-character hash prefix is sent.

Task<PwnedPassword> GetPwnedPasswordAsync(
    string plainTextPassword,
    bool addPadding = false,
    CancellationToken ct = default)
Parameters
NameTypeDescription
plainTextPasswordrequiredstringThe candidate password. Hashed locally; never transmitted.
addPaddingboolPad the response to 800-1000 records for extra privacy on the wire. Defaults to false.
ctCancellationTokenSignals cancellation.
Returns
Task<PwnedPassword>Always a value. IsPwned and PwnedCount describe the result; PwnedCount is 0 on no match.
Example usageC#
public sealed class SignupValidator(IPwnedPasswordsClient passwords)
{
    public async Task<bool> IsAcceptableAsync(string candidate)
    {
        PwnedPassword result =
            await passwords.GetPwnedPasswordAsync(candidate, addPadding: true);

        return result.IsPwned is not true;
    }
}

Calling the snippet above yields the following payload:

Sample outputjson
{
  "PlainTextPassword": "P@ssw0rd!",
  "IsPwned": true,
  "PwnedCount": 73586,
  "HashedPassword": "21BD1...A0F9E"
}
Exceptions
TypeThrown when
ArgumentNullExceptionplainTextPassword is null or empty.
PwnedApiExceptionAny non-success status while fetching the hash range.
Status codes
CodeMeaning
200Range returned. A non-matching suffix simply yields IsPwned = false.
429Rate limit exceeded. The Retry-After header is surfaced on PwnedApiException.RetryAfter.
503Service temporarily unavailable. Retried by the resilience pipeline when configured.

Never log or persist the plaintext password or the full hash.

Enable addPadding to obscure the size of the returned range from any on-path observer.

GetPwnedPasswordWithNtlmAsyncNone

Identical flow to GetPwnedPasswordAsync but hashes with NTLM. Use it when validating against Active Directory style hashes.

GETapi.pwnedpasswords.com/range/{hashPrefix}?mode=ntlmRequest detailsHide details
HTTP requestHTTP/1.1
GET /range/{hashPrefix}?mode=ntlm HTTP/1.1
Host: api.pwnedpasswords.com
Accept: text/plain
AuthenticationNone

This public endpoint is called without an API key or authorization header.

Response bodyPlain-text hash range

The SDK parses the suffix/count rows in memory and returns only the local match.

Official HIBP reference
Try this endpointWatch the same privacy-preserving work the SDK performs inside your process.

The demo mirrors pwned.client: your password stays in this browser tab and only a five-character hash prefix is sent.

Task<PwnedPassword> GetPwnedPasswordWithNtlmAsync(
    string plainTextPassword,
    bool addPadding = false,
    CancellationToken ct = default)
Parameters
NameTypeDescription
plainTextPasswordrequiredstringThe candidate password. Hashed with NTLM locally.
addPaddingboolPad the response for extra privacy. Defaults to false.
ctCancellationTokenSignals cancellation.
Returns
Task<PwnedPassword>The evaluation result using NTLM hashing.
Example usageC#
PwnedPassword result =
    await passwords.GetPwnedPasswordWithNtlmAsync("Winter2024");

Console.WriteLine(result.IsPwned is true
    ? $"Seen {result.PwnedCount:N0} times."
    : "Not found.");

Running the snippet above prints:

Sample outputtext
Seen 4,112 times.
Exceptions
TypeThrown when
ArgumentNullExceptionplainTextPassword is null or empty.
PwnedApiExceptionAny non-success status while fetching the hash range.
Status codes
CodeMeaning
200Range returned and evaluated locally.
429Rate limit exceeded. The Retry-After header is surfaced on PwnedApiException.RetryAfter.
503Service temporarily unavailable. Retried by the resilience pipeline when configured.

IsPasswordPwnedAsync (extension)None

A convenience extension on IPwnedPasswordsClient that returns a simple tuple when you only care about the yes/no answer and the count.

GETapi.pwnedpasswords.com/range/{hashPrefix}Request detailsHide details
HTTP requestHTTP/1.1
GET /range/{hashPrefix} HTTP/1.1
Host: api.pwnedpasswords.com
Accept: text/plain
AuthenticationNone

This public endpoint is called without an API key or authorization header.

Response bodyPlain-text hash range

The SDK parses the suffix/count rows in memory and returns only the local match.

Official HIBP reference
Try this endpointWatch the same privacy-preserving work the SDK performs inside your process.

The demo mirrors pwned.client: your password stays in this browser tab and only a five-character hash prefix is sent.

Task<(bool? IsPwned, long? Count)> IsPasswordPwnedAsync(
    this IPwnedPasswordsClient client,
    string password,
    CancellationToken ct = default)
Parameters
NameTypeDescription
passwordrequiredstringThe candidate password.
ctCancellationTokenSignals cancellation.
Returns
Task<(bool? IsPwned, long? Count)>A tuple of the pwned flag and the occurrence count.
Example usageC#
var (isPwned, count) = await passwords.IsPasswordPwnedAsync("hunter2");

Console.WriteLine($"Pwned: {isPwned}, count: {count}.");

Running the snippet above prints:

Sample outputtext
Pwned: True, count: 25891.
Exceptions
TypeThrown when
ArgumentNullExceptionpassword is null or empty.
PwnedApiExceptionAny non-success status while fetching the hash range.
Status codes
CodeMeaning
200Range returned and evaluated locally.
429Rate limit exceeded. The Retry-After header is surfaced on PwnedApiException.RetryAfter.
503Service temporarily unavailable. Retried by the resilience pipeline when configured.
Privacy note

Never log or export plaintext passwords. K-anonymity range results that do not match the requested value must not be retained.