Python developers have a few solid built-in libraries for email validation. email-validator, django.core.validators, plain regex. They all confirm the address looks valid. None tell you whether the mailbox is real. This guide bridges the gap with copy-paste integrations for Flask and Django.
The starting point: email-validator
Most Python projects use the email-validator library. It checks syntax, normalizes case, and optionally does DNS.
from email_validator import validate_email, EmailNotValidError
try:
info = validate_email(raw_email, check_deliverability=True)
email = info.normalized
except EmailNotValidError as e:
return {'error': str(e)}
check_deliverability=True runs an MX lookup, so it catches obviously-bad domains. It does not verify the specific mailbox.
The real verification call
import os
import requests
def verify_email(address: str) -> dict:
response = requests.post(
'https://mailoclean.com/api/v1/verify',
headers={
'Authorization': f'Bearer {os.environ["MAILOCLEAN_KEY"]}',
'Content-Type': 'application/json',
},
json={'email': address},
timeout=5,
)
response.raise_for_status()
return response.json()
Wiring into Flask
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.post('/signup')
def signup():
email = request.json.get('email', '').strip().lower()
if not email:
return jsonify(error='Email is required.'), 422
try:
result = verify_email(email)
except requests.RequestException:
result = {'status': 'unknown'}
if result.get('status') in ('invalid', 'disposable'):
return jsonify(error='Please use a valid, permanent email address.'), 422
user = create_user(email)
return jsonify(user=user)
Wiring into Django
Cleanest with a custom validator:
# validators.py
from django.core.exceptions import ValidationError
from django.core.cache import cache
import requests, os
def verify_real_email(value):
cache_key = f'verify:{value.lower()}'
result = cache.get(cache_key)
if result is None:
try:
r = requests.post(
'https://mailoclean.com/api/v1/verify',
headers={'Authorization': f'Bearer {os.environ["MAILOCLEAN_KEY"]}'},
json={'email': value},
timeout=5,
)
result = r.json()
cache.set(cache_key, result, 86400)
except requests.RequestException:
return # fail open
if result.get('status') in ('invalid', 'disposable'):
raise ValidationError('Please use a valid, permanent email address.')
Then in your form or serializer:
# forms.py
from django import forms
from .validators import verify_real_email
class SignupForm(forms.Form):
email = forms.EmailField(validators=[verify_real_email])
Async with Django
If you want verification to be non-blocking, run it in Celery after account creation:
@shared_task
def verify_user_email(user_id):
user = User.objects.get(pk=user_id)
result = verify_email(user.email)
user.email_status = result.get('status', 'unknown')
user.email_score = result.get('score')
user.save()
Bulk verification in Python
For lists, hit the bulk endpoint instead of looping. Submit a single batch, poll for status, download the result.
r = requests.post(
'https://mailoclean.com/api/v1/bulk',
headers={'Authorization': f'Bearer {os.environ["MAILOCLEAN_KEY"]}'},
json={'emails': '\n'.join(emails_list)},
)
batch_id = r.json()['id']
# poll /api/v1/bulk/{id} until status is 'done'
FAQ
What about the verify-email PyPI package?
It works for syntax and MX, same as email-validator. Neither runs an SMTP handshake. For real mailbox verification you need an external service like MailoClean.
Is the API call slow inside a request handler?
Median 1.4 seconds. For synchronous signup endpoints, that is acceptable. For latency-sensitive flows, verify async after account creation.
Plug it in
Grab a key from the dashboard, set the env var, paste the verifier function. Five-minute integration in Flask or Django.