Domains and stealer logs
Verify control of a domain, review the domains on your dashboard, and search stealer-log exposure. Domain search returns sensitive breaches, so every operation here is authenticated.
IPwnedDomainClientDomain verification and domain-wide breach search.IPwnedStealerLogsClientStealer-log search by email, website domain, or email domain. Requires a Pro subscription or higher.
Inject IPwnedDomainClient and IPwnedStealerLogsClient. Both need an API key; stealer logs additionally require a Pro subscription and a verified domain on your dashboard.
using HaveIBeenPwned.Client;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddPwnedServices(options =>
{
options.ApiKey = builder.Configuration["Hibp:ApiKey"];
options.SubscriptionLevel = HibpSubscriptionLevel.Pro2;
options.UserAgent = "MyApp/1.0";
});
var app = builder.Build();GenerateDomainVerificationDnsTokenAsyncAPI key
Requests the DNS TXT value you publish to prove control of a domain before it can be searched.
POSThaveibeenpwned.com/api/v3/domainverification/generatednstokenRequest detailsHide details
POST /api/v3/domainverification/generatednstoken HTTP/1.1Host: haveibeenpwned.comAccept: application/jsonhibp-api-key: ••••••••••••The SDK reads the configured key and adds the hibp-api-key header for this request.
The SDK deserializes the response into the documented .NET return type.
Task<DomainVerificationDnsToken> GenerateDomainVerificationDnsTokenAsync(
string domain,
CancellationToken ct = default)| Name | Type | Description |
|---|---|---|
domainrequired | string | The apex domain to verify. |
ct | CancellationToken | Signals cancellation. |
Task<DomainVerificationDnsToken> | Carries TxtRecordValue, the string to publish as a DNS TXT record. |
DomainVerificationDnsToken token =
await domains.GenerateDomainVerificationDnsTokenAsync("contoso.com");
Console.WriteLine($"Publish TXT: {token.TxtRecordValue}");Running the snippet above prints:
Publish TXT: hibp-verify=6f1c9a2b8e4d47f0a1b2c3d4e5f60718| Type | Thrown when |
|---|---|
ArgumentNullException | domain is null, empty, or whitespace. |
PwnedApiException | Any non-success status. |
| Code | Meaning |
|---|---|
| 200 | Token generated. |
| 401 | Missing or invalid API key. |
| 429 | Rate limit exceeded. The Retry-After header is surfaced on PwnedApiException.RetryAfter. |
| 503 | Service temporarily unavailable. Retried by the resilience pipeline when configured. |
VerifyDomainViaDnsAsyncAPI key
Asks HIBP to check for the published TXT record and complete verification. Completes without a value on success.
POSThaveibeenpwned.com/api/v3/domainverification/verifydnstokenRequest detailsHide details
POST /api/v3/domainverification/verifydnstoken HTTP/1.1Host: haveibeenpwned.comAccept: application/jsonhibp-api-key: ••••••••••••The SDK reads the configured key and adds the hibp-api-key header for this request.
The SDK deserializes the response into the documented .NET return type.
Task VerifyDomainViaDnsAsync(
string domain,
CancellationToken ct = default)| Name | Type | Description |
|---|---|---|
domainrequired | string | The domain whose TXT record is ready to be checked. |
ct | CancellationToken | Signals cancellation. |
Task | Completes on success. Throws when the record is missing or invalid. |
await domains.VerifyDomainViaDnsAsync("contoso.com");
Console.WriteLine("Domain verified.");Running the snippet above prints:
Domain verified.| Type | Thrown when |
|---|---|
ArgumentNullException | domain is null, empty, or whitespace. |
PwnedApiException | Verification failed, for example the TXT record was not found (400). |
| Code | Meaning |
|---|---|
| 200 | Verification succeeded. |
| 400 | The TXT record was missing or did not match. |
| 401 | Missing or invalid API key. |
| 429 | Rate limit exceeded. The Retry-After header is surfaced on PwnedApiException.RetryAfter. |
| 503 | Service temporarily unavailable. Retried by the resilience pipeline when configured. |
SendDomainVerificationEmailAsyncAPI key
Sends a verification message to a standard administrative alias when DNS verification is not an option.
POSThaveibeenpwned.com/api/v3/domainverification/sendemailRequest detailsHide details
POST /api/v3/domainverification/sendemail HTTP/1.1Host: haveibeenpwned.comAccept: application/jsonhibp-api-key: ••••••••••••The SDK reads the configured key and adds the hibp-api-key header for this request.
The SDK deserializes the response into the documented .NET return type.
Task SendDomainVerificationEmailAsync(
string domain,
DomainVerificationEmailAlias emailAlias,
CancellationToken ct = default)| Name | Type | Description |
|---|---|---|
domainrequired | string | The domain to verify. |
emailAliasrequired | DomainVerificationEmailAlias | One of Admin, Hostmaster, Info, Security, or Webmaster. |
ct | CancellationToken | Signals cancellation. |
Task | Completes once the verification email is queued. |
await domains.SendDomainVerificationEmailAsync(
"contoso.com",
DomainVerificationEmailAlias.Security);
Console.WriteLine("Verification email sent to security@contoso.com.");Running the snippet above prints:
Verification email sent to security@contoso.com.| Type | Thrown when |
|---|---|
ArgumentNullException | domain is null, empty, or whitespace. |
PwnedApiException | Any non-success status. |
| Code | Meaning |
|---|---|
| 200 | Email queued. |
| 401 | Missing or invalid API key. |
| 429 | Rate limit exceeded. The Retry-After header is surfaced on PwnedApiException.RetryAfter. |
| 503 | Service temporarily unavailable. Retried by the resilience pipeline when configured. |
GetBreachedDomainAsyncAPI key + verified domain
Returns every breached alias on a verified domain, mapped to the breaches each appeared in. Because control is proven, sensitive breaches are included.
GEThaveibeenpwned.com/api/v3/breacheddomain/{domain}Request detailsHide details
GET /api/v3/breacheddomain/{domain} HTTP/1.1Host: haveibeenpwned.comAccept: application/jsonhibp-api-key: ••••••••••••The SDK reads the configured key and adds the hibp-api-key header for this request.
The SDK deserializes the response into the documented .NET return type.
Task<DomainBreaches?> GetBreachedDomainAsync(
string domain,
CancellationToken ct = default)| Name | Type | Description |
|---|---|---|
domainrequired | string | A domain already verified on your dashboard. |
ct | CancellationToken | Signals cancellation. |
Task<DomainBreaches?> | A dictionary of alias to breach names (DomainBreaches derives from Dictionary<string, string[]>), or null when nothing is found. |
DomainBreaches? results = await domains.GetBreachedDomainAsync("contoso.com");
foreach (var (alias, breaches) in results ?? [])
{
Console.WriteLine($"{alias}@contoso.com: {string.Join(", ", breaches)}");
}Running the snippet above prints:
jsmith@contoso.com: Adobe, LinkedIn
mvargas@contoso.com: Dropbox| Type | Thrown when |
|---|---|
ArgumentNullException | domain is null, empty, or whitespace. |
PwnedApiException | Any non-success status other than 404. |
| Code | Meaning |
|---|---|
| 200 | Breached aliases returned. |
| 401 | Missing or invalid API key. |
| 403 | The domain is not verified for this key. |
| 404 | No breached aliases. Returned as null. |
| 429 | Rate limit exceeded. The Retry-After header is surfaced on PwnedApiException.RetryAfter. |
| 503 | Service temporarily unavailable. Retried by the resilience pipeline when configured. |
The size of domain you can search is capped by your subscription's DomainSearchMaxBreachedAccounts.
GetSubscribedDomainsAsyncAPI key
Lists the domains associated with your API key, along with their last-known breach counts and renewal dates.
GEThaveibeenpwned.com/api/v3/subscribeddomainsRequest detailsHide details
GET /api/v3/subscribeddomains HTTP/1.1Host: haveibeenpwned.comAccept: application/jsonhibp-api-key: ••••••••••••The SDK reads the configured key and adds the hibp-api-key header for this request.
The SDK deserializes the response into the documented .NET return type.
Task<SubscribedDomain[]> GetSubscribedDomainsAsync(CancellationToken ct = default)| Name | Type | Description |
|---|---|---|
ct | CancellationToken | Signals cancellation. |
Task<SubscribedDomain[]> | Verified domains on the account, or an empty array. |
| Streams | Also available as GetSubscribedDomainsAsAsyncEnumerable for IAsyncEnumerable consumption. |
SubscribedDomain[] domainsOwned = await domains.GetSubscribedDomainsAsync();
foreach (var domain in domainsOwned)
{
Console.WriteLine($"{domain.DomainName}: {domain.PwnCount:N0} pwned.");
}Running the snippet above prints:
contoso.com: 128 pwned.
fabrikam.com: 0 pwned.| Type | Thrown when |
|---|---|
PwnedApiException | Any non-success status. |
| Code | Meaning |
|---|---|
| 200 | Domains returned. |
| 401 | Missing or invalid API key. |
| 429 | Rate limit exceeded. The Retry-After header is surfaced on PwnedApiException.RetryAfter. |
| 503 | Service temporarily unavailable. Retried by the resilience pipeline when configured. |
GetStealerLogsByEmailAsyncAPI key + Pro subscription
Returns the website domains where an email address was captured by an info stealer. The address must live on a verified domain.
GEThaveibeenpwned.com/api/v3/stealerlogsbyemail/{emailAddress}Request detailsHide details
GET /api/v3/stealerlogsbyemail/{emailAddress} HTTP/1.1Host: haveibeenpwned.comAccept: application/jsonhibp-api-key: ••••••••••••The SDK reads the configured key and adds the hibp-api-key header for this request.
The SDK deserializes the response into the documented .NET return type.
Task<string[]?> GetStealerLogsByEmailAsync(
string emailAddress,
CancellationToken ct = default)| Name | Type | Description |
|---|---|---|
emailAddressrequired | string | The email address to search. Must be on a verified domain. |
ct | CancellationToken | Signals cancellation. |
Task<string[]?> | Website domains, sorted alphabetically, or null when none are found. |
string[]? sites =
await stealerLogs.GetStealerLogsByEmailAsync("jsmith@contoso.com");
Console.WriteLine(sites is { Length: > 0 }
? string.Join(", ", sites)
: "No stealer-log exposure.");Running the snippet above prints:
github.com, netflix.com, okta.com| Type | Thrown when |
|---|---|
ArgumentNullException | emailAddress is null, empty, or whitespace. |
PwnedApiException | Any non-success status other than 404. |
| Code | Meaning |
|---|---|
| 200 | Stealer-log domains returned. |
| 401 | Missing or invalid API key. |
| 403 | The subscription does not include stealer logs, or the domain is not verified. |
| 404 | No exposure found. Returned as null. |
| 429 | Rate limit exceeded. The Retry-After header is surfaced on PwnedApiException.RetryAfter. |
| 503 | Service temporarily unavailable. Retried by the resilience pipeline when configured. |
All stealer-log operations require a Pro subscription or higher (SubscriptionStatus.IncludesStealerLogs is true).
GetStealerLogsByWebsiteDomainAsyncAPI key + Pro subscription
Returns the email addresses captured against a website domain in stealer logs. Useful for operators triaging account-takeover risk.
GEThaveibeenpwned.com/api/v3/stealerlogsbywebsitedomain/{domain}Request detailsHide details
GET /api/v3/stealerlogsbywebsitedomain/{domain} HTTP/1.1Host: haveibeenpwned.comAccept: application/jsonhibp-api-key: ••••••••••••The SDK reads the configured key and adds the hibp-api-key header for this request.
The SDK deserializes the response into the documented .NET return type.
Task<string[]?> GetStealerLogsByWebsiteDomainAsync(
string domain,
CancellationToken ct = default)| Name | Type | Description |
|---|---|---|
domainrequired | string | The website domain that appears in stealer-log URLs. |
ct | CancellationToken | Signals cancellation. |
Task<string[]?> | Email addresses, sorted alphabetically, or null when none are found. |
string[]? accounts =
await stealerLogs.GetStealerLogsByWebsiteDomainAsync("contoso.com");
Console.WriteLine($"{accounts?.Length ?? 0} accounts at risk.");Running the snippet above prints:
47 accounts at risk.| Type | Thrown when |
|---|---|
ArgumentNullException | domain is null, empty, or whitespace. |
PwnedApiException | Any non-success status other than 404. |
| Code | Meaning |
|---|---|
| 200 | Stealer-log accounts returned. |
| 401 | Missing or invalid API key. |
| 403 | The subscription does not include stealer logs, or the domain is not verified. |
| 404 | No exposure found. Returned as null. |
| 429 | Rate limit exceeded. The Retry-After header is surfaced on PwnedApiException.RetryAfter. |
| 503 | Service temporarily unavailable. Retried by the resilience pipeline when configured. |
GetStealerLogsByEmailDomainAsyncAPI key + Pro subscription
Maps each email alias on a verified domain to the website domains found in stealer logs. Ideal for organization-wide exposure reports.
GEThaveibeenpwned.com/api/v3/stealerlogsbyemaildomain/{domain}Request detailsHide details
GET /api/v3/stealerlogsbyemaildomain/{domain} HTTP/1.1Host: haveibeenpwned.comAccept: application/jsonhibp-api-key: ••••••••••••The SDK reads the configured key and adds the hibp-api-key header for this request.
The SDK deserializes the response into the documented .NET return type.
Task<StealerLogsByEmailDomain?> GetStealerLogsByEmailDomainAsync(
string domain,
CancellationToken ct = default)| Name | Type | Description |
|---|---|---|
domainrequired | string | The email domain to search. Must be verified on your dashboard. |
ct | CancellationToken | Signals cancellation. |
Task<StealerLogsByEmailDomain?> | A dictionary of alias to website domains (derives from Dictionary<string, string[]>), or null when none are found. |
StealerLogsByEmailDomain? logs =
await stealerLogs.GetStealerLogsByEmailDomainAsync("contoso.com");
foreach (var (alias, sites) in logs ?? [])
{
Console.WriteLine($"{alias}: {sites.Length} sites.");
}Running the snippet above prints:
jsmith: 3 sites.
mvargas: 1 sites.| Type | Thrown when |
|---|---|
ArgumentNullException | domain is null, empty, or whitespace. |
PwnedApiException | Any non-success status other than 404. |
| Code | Meaning |
|---|---|
| 200 | Stealer-log map returned. |
| 401 | Missing or invalid API key. |
| 403 | The subscription does not include stealer logs, or the domain is not verified. |
| 404 | No exposure found. Returned as null. |
| 429 | Rate limit exceeded. The Retry-After header is surfaced on PwnedApiException.RetryAfter. |
| 503 | Service temporarily unavailable. Retried by the resilience pipeline when configured. |
Never log or export plaintext passwords. K-anonymity range results that do not match the requested value must not be retained.