Security
Reading a breach record: a field-by-field guide for developers
A field-by-field guide to the email breach record: DataClasses, trust flags, stealer logs, BreachDate vs AddedDate, PwnCount, and risk rules.

An email breach check returns more than a yes or no. Each match comes with a structured record describing the incident, and those fields are what let you tell a decade-old forum leak apart from a fresh stealer log full of passwords. This guide walks through every field Finowl returns and shows how to turn them into concrete risk decisions.
The record at a glance
When an address is found, POST /v1/breaches/email returns breached: true and an array of records like this one:
{
"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
}The record describes the breach, not the person. Every address in the same incident gets the same record. What makes it specific to your user is the fact that their address was included. If you are new to breach checks, start with how email breach checks work.
Identity fields
Name, Title and Domain
Name is a stable identifier for the breach. Use it as the key when you store or compare results, for example to detect a breach that is new since your last check. Title is the human-readable name to show users. Domain is the website associated with the incident, when there is one; some records, such as aggregated lists, may not map to a single site.
Description
Description is a short narrative of what happened. It is useful in an admin view or a support tool. If you show it to end users, render it as text and keep your interface consistent with your own wording rather than relying on it.
What was exposed: DataClasses
DataClasses is a list of the kinds of data the breach contained, such as "Email addresses", "Passwords", "Usernames", "Phone numbers" or "IP addresses". It is the single most important field for risk decisions.
A breach containing only email addresses tells you the address is known to spammers. A breach containing passwords tells you a password this person once used may be in the hands of attackers, which is the raw material for credential stuffing. Other classes, such as physical addresses or dates of birth, matter for phishing and identity verification risk.
Match on exact strings and treat the list as open-ended. New classes can appear, so write rules that look for the classes you care about rather than enumerating all possible values.
Trust flags
These booleans tell you how much weight to give a record.
IsVerified
true means the breach has been confirmed as genuine. Unverified breaches are included in Finowl results, so this flag is essential. A reasonable default is to drive automated decisions only from verified records and show unverified ones as context.
IsFabricated
true means the data is believed to be fake, for example a dump assembled to look like a breach that never happened. Exclude fabricated records from risk scoring entirely.
IsSpamList
true means the dataset is a list of addresses used for spam rather than a breach of a service. It usually confirms the address is deliverable and circulating, but it rarely carries credentials. Weight it low for account security.
IsRetired
true means the breach has been retired from active use in the dataset. Treat retired records as historical and give them little or no weight in automated decisions.
Sensitivity: IsSensitive
IsSensitive marks breaches whose very existence can be embarrassing or harmful to reveal, such as a leak from a site where membership itself is private. The record is valid for risk scoring, but be careful how you present it. Do not show sensitive breach names in notifications that others might see, in emails that could be forwarded, or to anyone other than the account holder. A generic message like "your email appeared in a breach that included passwords" protects the user while still being actionable.
Malware-derived data: IsMalware and IsStealerLog
Most breaches come from a compromised service. These two flags point to something different.
IsMalware indicates the data was obtained through malware. IsStealerLog indicates it came from information-stealing malware logs, which are collected from infected devices and typically record the sites a person visited along with the usernames and passwords they typed.
That makes stealer log records a sharper signal than a typical breach. They are usually tied to a specific device, they often contain current credentials, and they suggest the user's machine may still be infected. A password reset alone may not be enough if the device is still compromised, so it is worth recommending a malware scan in your messaging.
Timing: BreachDate vs AddedDate
These two dates answer different questions.
| Field | Meaning | Use it for |
|---|---|---|
BreachDate |
When the incident happened | Judging how stale the exposed data is |
AddedDate |
When the record was added to the dataset | Detecting newly surfaced breaches |
BreachDate is a date, and it is often approximate. A breach from 2014 exposed passwords that many users have since changed, so its risk weight should decay over time.
AddedDate is a full timestamp. Breaches are frequently discovered or published long after they happen, so a record can be old by BreachDate but new by AddedDate. For monitoring, AddedDate is the field that tells you something changed since your last check. The email breach monitoring guide uses it for exactly that.
Scale: PwnCount
PwnCount is the number of accounts affected by the breach. It is context, not a risk signal for the individual. A huge breach is more likely to be in attacker tooling, but a small, targeted breach can be just as damaging for the people in it. Use PwnCount for display and for prioritizing internal review, not for scoring a single account.
Turning records into risk decisions
With the fields understood, you can build a small, explainable scoring function. The example below filters out records that should not count, then derives a few signals and a level.
function assessBreaches(result) {
const relevant = result.breaches.filter(
(b) => b.IsVerified && !b.IsFabricated && !b.IsRetired && !b.IsSpamList
);
const twoYearsAgo = new Date();
twoYearsAgo.setFullYear(twoYearsAgo.getFullYear() - 2);
const passwordExposed = relevant.some((b) => b.DataClasses.includes("Passwords"));
const recentPasswordExposure = relevant.some(
(b) => b.DataClasses.includes("Passwords") && new Date(b.BreachDate) >= twoYearsAgo
);
const stealerLog = result.breaches.some((b) => b.IsStealerLog || b.IsMalware);
const hasSensitive = relevant.some((b) => b.IsSensitive);
let level = "low";
if (passwordExposed) level = "elevated";
if (recentPasswordExposure || stealerLog) level = "high";
return { level, passwordExposed, stealerLog, hasSensitive, count: relevant.length };
}
async function main() {
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: "alex@example.com" }),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
console.log(assessBreaches(await res.json()));
}
main();Note that stealer log and malware flags are checked across all records, not just verified ones, because they are high-signal even when unconfirmed. Adjust that choice to your own risk tolerance.
Mapping levels to actions
- Low: no action, or a gentle prompt to enable two-factor authentication.
- Elevated: prompt for two-factor and check the account's current password against breach data, as described in credential stuffing and breached passwords.
- High: require a password change, offer two-factor, and for stealer logs, suggest checking the device for malware.
Keep it explainable
Store the reasons along with the level, such as "verified breach with passwords in the last two years." Support teams and users both need to understand why an account was flagged, and simple rules are easier to audit than opaque scores.
Storage and privacy
Store only what you need: usually the breach Name, AddedDate and your derived level. Avoid copying full descriptions into every user row, and treat sensitive breach names with extra care. The data privacy documentation covers how Finowl handles lookups, and each email check costs one credit, as detailed on the pricing page.
Frequently asked questions
What is the difference between BreachDate and AddedDate?
BreachDate is when the incident happened, while AddedDate is when the record was added to the dataset. Use BreachDate to judge staleness and AddedDate to detect newly surfaced breaches.
Should I count unverified breaches?
Use them as context, but drive automated actions mainly from records where IsVerified is true. Stealer log and malware records are an exception many teams still act on.
What does IsSensitive mean for how I notify users?
The breach is valid, but revealing its name could harm the user. Notify only the account holder and use generic wording rather than the breach title.
Is PwnCount a measure of risk for my user?
Not directly. It describes how large the breach was, which is useful context, but the exposed DataClasses and recency matter far more for an individual account.
Why are stealer log records treated as high risk?
They come from malware on a user's device and often contain recent, site-specific credentials. They can also mean the device is still infected.


