Framework · Django
Add Finowl breach and gender checks to Django
Django already has hooks for the places Finowl fits best. Password checks belong in AUTH_PASSWORD_VALIDATORS, where they run alongside the built-in length and similarity validators in the admin, in UserCreationForm and in password change views. Email breach checks fit a form's clean_email method, and bulk gender enrichment suits a management command you can run from cron or a scheduler. None of it needs middleware or a third-party Django package.
All three examples share a small finowl.py module inside your app that wraps requests with a module-level Session, a timeout and a single FinowlError exception. It reads FINOWL_API_KEY from the environment, so local settings, containers and your hosting platform's secret manager all work the same way. Network failures raise FinowlError with status 0, which lets each integration point choose between failing open, showing a form error or stopping a command.
STEP 1
A reusable Django client
Keep your key in the FINOWL_API_KEY environment variable and route every call through one helper, so authentication, timeouts and error handling live in one place.
pip install django requests# accounts/finowl.py
import os
import requests
BASE_URL = "https://api.finowl.co"
_session = requests.Session() # module-level: reuses connections across requests
class FinowlError(Exception):
def __init__(self, status, code, retry_after=None):
super().__init__(f"Finowl API error {status}: {code}")
self.status = status
self.code = code
self.retry_after = retry_after
def finowl_post(path, payload, timeout=10):
try:
response = _session.post(
f"{BASE_URL}{path}",
json=payload,
headers={"Authorization": f"Bearer {os.environ['FINOWL_API_KEY']}"},
timeout=timeout,
)
except requests.RequestException as exc:
raise FinowlError(0, "network_error") from exc
if response.ok:
return response.json()
try:
code = response.json()["error"]["code"]
except (ValueError, KeyError, TypeError):
code = "unknown_error"
retry_after = response.headers.get("Retry-After")
raise FinowlError(response.status_code, code, int(retry_after) if retry_after else None)STEP 2
Call the API you need

Check email breaches in a Django signup form
This example extends UserCreationForm with an email field and runs the Finowl lookup in clean_email, the hook Django calls after the field's own validation. A 400 response becomes a form error, because Finowl only accepts plain addresses with a dotted domain. Any other failure is logged and ignored so signups keep working. Breaches whose DataClasses include Passwords are collected with their Title and BreachDate for later use.

Breached password validator for Django
Django runs every class listed in AUTH_PASSWORD_VALIDATORS through validate_password, so a Finowl validator is checked automatically by UserCreationForm, SetPasswordForm, PasswordChangeForm, the admin and the createsuperuser command. The FinowlBreachedPasswordValidator class implements validate and get_help_text, sends the candidate password to /v1/breaches/password and raises ValidationError with the breach count when the password has been seen before.

Batch gender enrichment with a Django command
The enrich_gender management command loads customers whose gender_status is still empty, groups them into chunks of 50 and sends each chunk to /v1/gender/batch. Item ids are the customers' primary keys as strings, so each result maps straight back to a model instance. When a customer has a country code, it is added to the item as a hint, which helps with names that lean differently by country.
A FEW GOOD QUESTIONS
Django questions
Other languages and frameworks
Ship with Django today
100 free credits every month. Try every request in the sandbox first.