Flask
Getting Started
Flask's simplicity makes integrating user.cleaning straightforward. We'll use the requests library to communicate with our API, which you're likely already familiar with if you've worked with Flask before.
First, install the requests library:
pip install requestsNext, store your API key securely in your environment variables. Never hardcode API keys directly in your source code:
USER_CLEANING_API_KEY = "your-api-key"Next, load it in your application:
import os
USER_CLEANING_API_KEY = os.environ.get("USER_CLEANING_API_KEY", "")If you don't have an API key yet, check out our Quickstart guide to generate one.
IP Forwarding for Rate Limiting
For user.cleaning's rate limiting to work correctly, you need to forward the real client IP with each request. Add this helper function to extract the client IP:
from flask import request
def get_client_ip():
x_forwarded_for = request.headers.get("X-Forwarded-For")
if x_forwarded_for:
return x_forwarded_for.split(",")[0].strip()
return request.headers.get("X-Real-IP") or request.remote_addr or "unknown"Production: If you're behind nginx, Cloudflare, or a load balancer, X-Forwarded-For is set automatically.
Development/testing: Without a reverse proxy forwarding X-Forwarded-For, the integration will see your server's IP instead of actual client IPs. This means all requests appear to come from the same IP, and after 5 disposable email attempts, all registrations will be blocked until the IP is unbanned.
If you experience unexpected registration blocks, verify your reverse proxy is forwarding client IPs correctly. You can unban blocked IPs at API Settings and configure number of registration attempts before block or disable it completely at Color Configuration.
Creating the Validation Function
Let's create a helper function that handles communication with the user.cleaning API:
import os
import requests
from config import USER_CLEANING_API_KEY
def check_email(email: str, client_ip: str) -> dict:
try:
response = requests.post(
"https://api.user.cleaning/v1/external-api-requests/check-email",
params={"email": email},
headers={
"X-API-Key": USER_CLEANING_API_KEY,
"X-Forwarded-For": client_ip,
"X-Real-IP": client_ip,
},
timeout=5
)
if response.status_code == 403:
data = response.json()
return {"category": "banned", "message": data.get("detail", "Access denied")}
response.raise_for_status()
return response.json()
except requests.RequestException:
return {"category": "white"}The API responds with a JSON object containing a category field that tells you how to handle the email address.
Integration Approaches
Validating in Route Handlers
The most straightforward approach is to call the validation function directly in your route handlers and use Flask's abort() function to reject disposable emails:
from flask import Flask, request, jsonify, abort
app = Flask(__name__)
@app.route("/register", methods=["POST"])
def register():
email = request.form.get("email")
client_ip = get_client_ip()
result = check_email(email, client_ip)
if result["category"] == "banned":
abort(403, description=result["message"])
if result["category"] == "black":
abort(400, description="Disposable emails are not allowed")
if result["category"] == "grey":
# Suspicious email — let them in but require phone verification
user = create_user(email, requires_phone_verification=True)
return jsonify({"message": "Please enter your phone number for verification"}), 201
# 'white' — all good
user = create_user(email, requires_phone_verification=False)
return jsonify({"user_id": user.id}), 201This approach works well when you have just a few routes that need email validation and want direct control over the logic.
Creating Reusable Middleware
If you need to validate emails across multiple routes, creating a decorator function keeps your code DRY and consistent:
from flask import request, jsonify
from functools import wraps
def validate_email_middleware(f):
@wraps(f)
def decorated(*args, **kwargs):
email = request.form.get("email") or request.json.get("email")
if email:
client_ip = get_client_ip()
result = check_email(email, client_ip)
if result["category"] == "banned":
return jsonify({"error": result["message"]}), 403
if result["category"] == "black":
return jsonify({"error": "Disposable emails are not allowed"}), 400
# Attach result for use in route handler
request.email_validation = result
return f(*args, **kwargs)
return decoratedNow you can easily apply this validation to any route:
@app.route("/register", methods=["POST"])
@validate_email_middleware
def register():
requires_phone_verification = request.email_validation["category"] == "grey"
user = create_user(email, requires_phone_verification=requires_phone_verification)
return jsonify({"user_id": user.id}), 201Integration with WTForms
If you're using Flask-WTF for form handling, you can create a custom validator. Note that WTForms validators don't have direct access to the request object, so we access it from Flask's context:
from flask import request
from wtforms import ValidationError
def validate_not_disposable(form, field):
client_ip = get_client_ip()
result = check_email(field.data, client_ip)
if result["category"] == "banned":
raise ValidationError(result["message"])
if result["category"] == "black":
raise ValidationError("Please use a permanent email address")Then add this validator to your form definition:
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField
from wtforms.validators import Email, DataRequired
class RegistrationForm(FlaskForm):
email = StringField("Email", validators=[DataRequired(), Email(), validate_not_disposable])
password = PasswordField("Password", validators=[DataRequired()])When a user submits the form with a disposable email, they'll see your custom error message, and the form will remain on the page so they can correct their email address.
Understanding the Results
The category field can have three possible values:
- black — This is a disposable or temporary email address that should be blocked
- grey — Suspicious patterns were detected (like alias chains or plus addressing), consider requiring phone verification
- white — The email looks legitimate and can be accepted without restrictions
For a complete description of the output data structure and all available fields, see our Categories Reference.