# Django Email Validator and Fields

URL: https://email-validation.org/docs/guides/django/
Updated: 2026-11-10

> Documentation index: https://email-validation.org/llms.txt

A Django validator and form and model fields that fail an email address that cannot receive email. They are built on
the official [Python client library](https://email-validation.org/docs/sdks/python/), check each address once, and never stop a real person from signing up
when the API cannot answer in time.

```python
from django import forms
from email_validation_django import DeliverableEmailFormField

class SignupForm(forms.Form):
    email = DeliverableEmailFormField()
```

## Install

The package is not on PyPI yet. Download it here and install it with pip:

1. Download [email_validation_django-0.1.0-py3-none-any.whl](https://email-validation.org/downloads/django/email_validation_django-0.1.0-py3-none-any.whl).
2. Install it into your project's environment (pip installs Django and the Python client library from PyPI):

   ```bash
   pip install ./email_validation_django-0.1.0-py3-none-any.whl
   ```

   To keep it in your requirements, commit the file to your repository and list its path there.
3. Create an API key on the [API keys page](https://email-validation.org/app/api-keys/) with the permission "Validate addresses", and give it a
   [daily credit cap](https://email-validation.org/docs/authentication/#limits) to limit what a busy form can spend. Set it in your environment
   as `EV_API_KEY`; never put it in code. Each new address a visitor sends is a paid check, so also rate-limit the
   form's view per visitor (with a rate-limiting package or at your proxy).

It needs Django 4.2 or newer and Python 3.10 or newer. Nothing goes in `INSTALLED_APPS`.

## Use it

```python
from django import forms
from django.db import models
from email_validation_django import (
    DeliverableEmailField,
    DeliverableEmailFormField,
    DeliverableEmailValidator,
    validate_email_deliverable,
)

# a form field: forms.EmailField, then the check once the format is right
class SignupForm(forms.Form):
    email = DeliverableEmailFormField()
    work_email = DeliverableEmailFormField(fail_on=["invalid", "disposable"], fail_open=False, required=False)

# a model field: models.EmailField, checked in full_clean() and in model forms
class Customer(models.Model):
    email = DeliverableEmailField(unique=True)

# the validator on any field
email = forms.EmailField(validators=[validate_email_deliverable])
strict = DeliverableEmailValidator(fail_on=["invalid", "disposable"], messages={"disposable": "Please use your work address."})
```

A malformed address is refused by Django's own email check and never sent.

## Settings

All optional, in `settings.py`:

```python
EMAIL_VALIDATION = {
    # default: the EV_API_KEY environment variable
    "API_KEY": None,
    # default: EV_BASE_URL, else https://api.email-validation.org
    "BASE_URL": None,
    "FAIL_ON": ["invalid"],
    "BLOCK_DISPOSABLE": False,
    "FAIL_OPEN": True,
    "TIMEOUT": 5,
    "CLIENT_TIMEOUT": 8,
    "CACHE": "default",
    "CACHE_TTL": 3600,
}
```

- **Which statuses fail.** Only `invalid` by default. Set `"BLOCK_DISPOSABLE": True` to fail `disposable` addresses
  as well. `catch_all` and `unknown` pass unless you add them: see [what each status means](https://email-validation.org/docs/statuses/).
- **When the check cannot finish** (it timed out, the API could not be reached, it answered with a 5xx or 429 error,
  the account has no credits or no key is set), the address passes and a warning goes to the package's logger
  (named after the package) with the error code and request id, never the address. Set `"FAIL_OPEN": False` to fail
  the field with "We could not check this address right now" instead.
- **EU data residency.** Set `"BASE_URL": "https://api-eu.email-validation.org"`; the same key works on both.

## Speed and caching

The validator gives the API 5 seconds for the check (the API takes 5 to 15) and waits 8
seconds for the answer, without retrying: a slow mail server then answers `unknown`, which passes and is free.

Each result is kept in Django's cache for an hour, under a SHA-256 hash of the trimmed, lower-cased address, so a
form sent again after fixing another field is not checked or charged again. Only the status is kept, never the
address, and `unknown` results are not kept.

## Messages

The messages are translatable (lazy gettext strings): translate them in your project's locale files, or replace one with
the validator's `messages` argument. Each error has a code for your templates or API responses:

```text
email_invalid  email_disposable  email_catch_all  email_unknown  email_unavailable
```

The messages never show the API's own error text.

## Costs

Each completed check costs one credit. Results with the status `unknown` are free, and so are malformed addresses
and the [test addresses](https://email-validation.org/docs/test-addresses/): try the field with `accepted@sandbox.email-validation.org` (passes) and
`no_mailbox@sandbox.email-validation.org` (fails) at no cost. See [credits and billing](https://email-validation.org/docs/credits-and-billing/).

## Related

- [Validating sign-up forms](https://email-validation.org/docs/guides/signup-form-validation/): what to block and what to accept
- [The Python client library](https://email-validation.org/docs/sdks/python/) for other calls, such as bulk lists
