Python · Email breach checks
Check email breaches in Python with requests
The email breach endpoint takes one address and returns a breached flag plus a list of breach records. In Python that list arrives as plain dictionaries, so you can loop over it, filter on IsVerified, or turn DataClasses into a set to see whether passwords or phone numbers were exposed. The script below is a command-line checker you can drop into a cron job or call from a management command.
Error handling stays explicit. A 400 means the address failed validation (display names, quoted local parts and IP-address domains are rejected), a 401 points to a missing or wrong key, and a rate-limited 429 tells you how many seconds to wait in Retry-After. A 429 with quota_exceeded means you are out of credits, so the script stops instead of looping.
Steps
- 1
Install requests with pip install requests inside your virtual environment.
- 2
Create an API key in the Finowl dashboard and export it as FINOWL_API_KEY.
- 3
Save the script as check_email.py and run python3 check_email.py alex@example.com.
- 4
Read each breach's Title, BreachDate and DataClasses to decide what to tell the user.
- 5
Move check_email into your app and call it from a signup view or a scheduled task.
pip install requestsimport os
import sys
import time
import requests
API_URL = "https://api.finowl.co/v1/breaches/email"
HEADERS = {
"Authorization": f"Bearer {os.environ['FINOWL_API_KEY']}",
"Content-Type": "application/json",
}
def error_code(resp):
try:
return resp.json()["error"]["code"]
except (ValueError, KeyError, TypeError):
return "unknown_error"
def check_email(email, attempts=3):
for _ in range(attempts):
resp = requests.post(API_URL, json={"email": email}, headers=HEADERS, timeout=10)
if resp.status_code == 200:
return resp.json()
code = error_code(resp)
if resp.status_code == 400:
raise ValueError(f"Finowl rejected the address: {code}")
if resp.status_code == 401:
raise PermissionError("Unauthorized: check FINOWL_API_KEY")
if resp.status_code == 429 and code == "rate_limited":
time.sleep(int(resp.headers.get("Retry-After", "1")))
continue
raise RuntimeError(f"Finowl returned {resp.status_code}: {code}")
raise RuntimeError("Still rate limited after several attempts")
if __name__ == "__main__":
result = check_email(sys.argv[1] if len(sys.argv) > 1 else "alex@example.com")
if not result["breached"]:
print(f"{result['email']} does not appear in any known breach")
for breach in result["breaches"]:
flag = "" if breach["IsVerified"] else " [unverified]"
print(f"{breach['Title']} ({breach['BreachDate']}){flag}")
print(" exposed: " + ", ".join(breach["DataClasses"]))IN PRODUCTION
Production tips
Treat unverified breaches differently from verified ones. Filtering on IsVerified lets you show a softer warning, or skip the notification entirely, when a record has not been confirmed.
When rechecking a large customer list from Celery, keep a single requests.Session per worker process and space calls out so the whole fleet stays under 60 requests per minute.
Store only what you need, such as breach Name and BreachDate, so a later recheck can detect new breaches without keeping the full Description text in your database.
Each lookup costs 1 credit, with 100 free credits every month. See pricing and error codes.
A FEW GOOD QUESTIONS
Common questions
Related guides
- Check breached passwords in Python at signup
- Predict gender from names in Python
- Check email breaches in Node.js with fetch
- Check email breaches in TypeScript with typed results
- Check email breaches in Go with net/http
- Check email breaches in Java with HttpClient
- Email breach checks for fintech signups and logins
- Email breach checks for online stores and shopper logins
- Email breach checks for SaaS logins and admins
Check email breaches in Python with requests
Grab an API key, paste the example and run it. 100 free credits every month.