Skip to content
Finowl

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.

Install
pip install django requests
Django client
# 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.