Block disposable email signups in FastAPI
Use a Pydantic validator or a FastAPI dependency. The async dependency runs concurrently with other dependencies, so latency stays minimal.
The code
# app/auth.py
import os, httpx
from fastapi import HTTPException, Depends
from pydantic import BaseModel, EmailStr
class SignupBody(BaseModel):
email: EmailStr
password: str
CDE_URL = "https://api.checkdisposable.email/v1/check"
CDE_KEY = os.environ["CDE_KEY"]
async def reject_disposable(body: SignupBody) -> SignupBody:
try:
async with httpx.AsyncClient(timeout=3.0) as client:
r = await client.get(
CDE_URL,
params={"email": body.email},
headers={"Authorization": f"Bearer {CDE_KEY}"},
)
if r.is_success and r.json().get("is_disposable"):
raise HTTPException(400, "Please use a real email address.")
except httpx.HTTPError:
pass # fail open
return body
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.post("/signup")
async def signup(body: SignupBody = Depends(reject_disposable)):
# ...create user
return {"ok": True}Notes
- Why a Depends, not a validator
- Pydantic validators run synchronously. The disposable check is I/O — async. Using a FastAPI dependency means the call is properly awaited without blocking the event loop.
- Type-safe + reusable
- The `reject_disposable` dependency returns the typed body, so any handler can drop it in via `Depends()` and still benefit from full Pydantic typing.
- Tie to email-only routes
- For OAuth callback routes, factor `is_disposable(email)` out as a plain async function and call it inside the OAuth flow instead of using `Depends`.
Get a free API key
500 checks/month, no credit card. No credit card. 30 seconds.
Sign up free →