Security
How email breach checks work and what they tell you
Learn what an email breach check is, how the lookup works behind one API call, what the breach record contains, and when to run checks in your product.

An email breach check answers one narrow question: has this address appeared in a known data breach? That sounds simple, but the answer carries a lot of useful context, from which sites leaked the address to which kinds of data went with it. This guide explains how a breach check works, what you get back from Finowl, and where it fits in a real product.
What an email breach check is
When a website or app is breached, the stolen data often ends up traded or published. Security researchers collect those datasets, verify them where possible, and index them so that people and services can ask whether a given email address is included.
An email breach check is a lookup against that index. You send an address, and the service returns either "not found" or a list of breaches that contained it. Each breach in the list is described with metadata: the name of the site, when the breach happened, how many accounts were affected, and what types of data were exposed.
The check does not return the leaked data itself. You will not see the user's old password, their home address, or anything else from the dump. You only learn which incidents the address appeared in and what categories of data those incidents exposed. That distinction matters for privacy and is what makes the check safe to use inside normal product flows.
How the lookup works
From your side, a breach check is a single HTTPS request. Behind it, a few things happen.
Normalizing and validating the address
Before any lookup, the address is validated. Finowl accepts a plain address with a dotted domain, such as alex@example.com. Display names (Alex <alex@example.com>), quoted local parts and IP-address domains are rejected, and the address can be at most 254 characters. A request that fails validation returns 400 with the code invalid_input and costs nothing, because it never ran.
Matching against breach data
The validated address is matched against Finowl Intelligence, the breach dataset behind the API. If the address appears in one or more indexed breaches, the response lists each of them. If it appears in none, the response says so explicitly.
Returning breach metadata
For each match, you get a structured record describing the breach. The record is the same for every address in that breach, so it tells you about the incident, not about the individual. Your code combines that incident data with the fact that this particular address was included.
Running a check with the Finowl API
All Finowl endpoints are POST requests with a JSON body and a bearer token. You can create a key from the dashboard at app.finowl.co and try requests in the Sandbox before writing any code.
curl -X POST https://api.finowl.co/v1/breaches/email \
-H "Authorization: Bearer $FINOWL_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "email": "alex@example.com" }'The same call in JavaScript, using the built-in fetch in Node 18 or later:
async function checkEmail(email) {
const res = await fetch("https://api.finowl.co/v1/breaches/email", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.FINOWL_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ email }),
});
if (!res.ok) {
const body = await res.json().catch(() => ({}));
throw new Error(`Breach check failed: ${body.error?.code ?? res.status}`);
}
return res.json();
}
checkEmail("alex@example.com").then((result) => {
console.log(result.breached, result.breaches.length);
});What you get back
A breached address returns breached: true and an array of breach records:
{
"email": "alex@example.com",
"breached": true,
"breaches": [
{
"Name": "ExampleSite",
"Title": "Example Site",
"Domain": "example.com",
"BreachDate": "2019-03-01",
"AddedDate": "2019-05-10T08:00:00Z",
"PwnCount": 1200000,
"Description": "...",
"DataClasses": ["Email addresses", "Passwords", "Usernames"],
"IsVerified": true,
"IsFabricated": false,
"IsSensitive": false,
"IsRetired": false,
"IsSpamList": false,
"IsMalware": false,
"IsStealerLog": false
}
]
}An address that was not found returns an empty list:
{ "email": "alex@example.com", "breached": false, "breaches": [] }A few fields do most of the work in practice:
| Field | What it tells you |
|---|---|
DataClasses |
Which kinds of data were exposed, such as passwords or phone numbers |
BreachDate |
When the incident happened |
IsVerified |
Whether the breach has been confirmed as genuine |
PwnCount |
How many accounts the breach affected |
Unverified breaches are included in the results, so always check IsVerified before treating a match as confirmed. For a full walkthrough of every field and how to weigh them, see reading a breach record.
What a breach check does not tell you
It helps to be precise about the limits.
- Not found does not mean safe. It means the address is not in any breach Finowl Intelligence has indexed. Many incidents are never disclosed or never surface.
- Found does not mean compromised today. A breach from years ago may involve a password the person changed long since. The check tells you about exposure, not about current account state.
- It is about the address, not the person. Shared inboxes, aliases and recycled addresses all complicate the picture. Treat the result as a signal, not a verdict.
Used with those limits in mind, the result is one of the cheapest, most reliable risk signals you can add to an account.
When to use an email breach check
At signup
Checking a new user's address at signup tells you whether that address has been exposed alongside passwords in the past. If it has, you can nudge the user toward a strong, unique password or offer two-factor authentication right away. Pair it with a password check (see how to block breached passwords at signup) for a stronger combined signal.
At login and on risk events
A breached address combined with an unusual login, such as a new device or country, is a reasonable trigger for step-up verification. The breach check itself does not detect an attack, but it raises the prior that an attacker holds a valid credential pair.
For ongoing monitoring
Breaches are added over time. Re-checking customer addresses on a schedule lets you notify users when their email shows up in a newly indexed incident. The email breach monitoring guide covers scheduling, comparing results with previous runs, and notifying users without alarming them.
For security reviews of your own team
Checking employee or admin addresses helps you spot accounts that deserve mandatory two-factor authentication or a password reset before they become an entry point.
Practical considerations
Cost and rate limits
Each email lookup costs one credit. Every account gets 100 free credits each month, and prepaid packs never expire, so occasional checks are inexpensive. The API allows 60 requests per minute per account; over that, you receive 429 with the code rate_limited and a Retry-After header. Wait that many seconds before retrying. Full details are on the pricing page.
Errors and retries
Retry 429 rate_limited after the Retry-After delay. Be more careful with 5xx and 504 responses: the lookup may have run and may have been charged, so avoid blind retry loops. The retries guide explains which errors are safe to repeat.
Privacy
An email address is personal data. Check only addresses you have a legitimate reason to check, mention breach screening in your privacy notice, and store the minimum you need, such as a breached flag and breach names, rather than full responses indefinitely.
Getting started
The fastest path is to sign up, grab an API key, and run the curl command above against a test address in the Sandbox. From there, wire the check into the flow where it adds the most value, usually signup or a periodic monitoring job. The email breach feature page summarizes the product, and the quickstart walks through authentication and your first request.
Frequently asked questions
Does an email breach check reveal the user's leaked password?
No. The response lists which breaches contained the address and what categories of data each breach exposed, but it never returns the leaked data itself.
Why are unverified breaches included in results?
Unverified breaches can still be useful context, so they are returned with IsVerified: false. Your code can decide whether to count them, ignore them, or weight them lower than confirmed incidents.
How much does an email breach check cost?
Each lookup costs one credit. Accounts receive 100 free credits every month, and prepaid packs start at 10,000 credits for $29.
What happens if I send an invalid email address?
The API returns 400 with the error code invalid_input. Requests rejected before running are not charged.
How often should I re-check an address?
It depends on your risk tolerance. Many teams check at signup and then on a regular schedule, such as monthly, so they can alert users when their address appears in a newly added breach.


