Tutoriel Python FastAPI Django API intégration validation email

Valider des emails en Python avec l'API Syvel

Guide complet pour intégrer la validation d'emails Syvel dans votre application Python : script standalone, FastAPI, Django et traitement batch de listes.

Par Équipe Syvel · · 8 min de lecture

Ce que vous allez apprendre

Ce guide couvre quatre scénarios d’intégration Python avec l’API Syvel :

  1. Script standalone : valider une adresse ou une liste depuis la ligne de commande
  2. FastAPI : validation asynchrone dans un endpoint d’inscription
  3. Django : validator custom pour les modèles et formulaires
  4. Batch processing : valider des milliers d’adresses de façon efficace

Prérequis : Python 3.9+, httpx pour les appels async (pip install httpx).


Votre clé API

Toutes les requêtes nécessitent votre clé API dans le header Authorization. Récupérez-la dans votre tableau de bord Syvel →. Consultez aussi la référence complète de l’endpoint /v1/check pour la liste exhaustive des champs retournés.

Terminal window
export SYVEL_API_KEY="sv_votre_cle_ici"

Le format de votre clé commence toujours par sv_. Ne la commitez jamais dans votre code — utilisez les variables d’environnement.


Réponse API : les champs clés

L’endpoint GET /v1/check/{email} retourne :

{
"email": "a9****[email protected]",
"is_risky": true,
"risk_score": 100,
"reason": "disposable",
"is_free_provider": false,
"is_corporate_email": false,
"did_you_mean": null,
"is_alias_email": false,
"mx_provider_label": "Yopmail",
"deliverability_score": 0
}

Les champs de décision principaux :

  • is_risky : true si le score dépasse le seuil (défaut : 65). C’est le champ de blocage recommandé.
  • risk_score (0–100) : pour un seuil personnalisé.
  • reason : safe · disposable · undeliverable · role_account.
  • did_you_mean : suggestion si typo détectée (gmial.comgmail.com).
  • deliverability_score (0–100) : probabilité de livraison effective.

Exemple 1 : Script standalone (synchrone)

Le moyen le plus rapide de tester l’API :

validate_email.py
import httpx
import os
import sys
API_KEY = os.environ.get("SYVEL_API_KEY")
BASE_URL = "https://api.syvel.io/v1"
def validate_email(email: str) -> dict:
"""Valide un email via l'API Syvel. Retourne le résultat complet."""
with httpx.Client(timeout=10.0) as client:
response = client.get(
f"{BASE_URL}/check/{email}",
headers={"Authorization": f"Bearer {API_KEY}"},
)
response.raise_for_status()
return response.json()
def main():
email = sys.argv[1] if len(sys.argv) > 1 else input("Email à valider : ")
result = validate_email(email)
print(f"\n📧 Analyse de : {email}")
print(f" Score de risque : {result['risk_score']}/100")
print(f" Score délivrabilité : {result['deliverability_score']}/100")
print(f" Risqué : {'⚠ Oui' if result['is_risky'] else '✓ Non'}")
print(f" Raison : {result['reason']}")
print(f" Provider MX : {result.get('mx_provider_label') or 'Inconnu'}")
if result.get('did_you_mean'):
print(f" Vouliez-vous dire : {result['did_you_mean']}")
if __name__ == "__main__":
main()
Terminal window
python validate_email.py [email protected]
# Output :
# 📧 Analyse de : [email protected]
# Score de risque : 100/100
# Score délivrabilité : 0/100
# Risqué : ⚠ Oui
# Raison : disposable
# Provider MX : Yopmail

Exemple 2 : FastAPI (asynchrone)

L’intégration idéale pour une API REST moderne. L’appel async ne bloque pas le thread principal :

app/api/v1/auth.py
import httpx
import os
import logging
from fastapi import APIRouter, HTTPException, status
from pydantic import BaseModel, EmailStr
router = APIRouter()
logger = logging.getLogger(__name__)
SYVEL_API_KEY = os.environ.get("SYVEL_API_KEY")
SYVEL_BASE_URL = "https://api.syvel.io/v1/check"
class SyvelResult(BaseModel):
risk_score: int
is_risky: bool
reason: str # "safe" | "disposable" | "undeliverable" | "role_account"
did_you_mean: str | None = None
deliverability_score: int
async def check_email_quality(email: str, client: httpx.AsyncClient) -> SyvelResult | None:
"""
Vérifie la qualité d'un email via Syvel.
Retourne None en cas d'erreur (fail open — ne jamais bloquer sur erreur externe).
"""
if not SYVEL_API_KEY:
logger.warning("SYVEL_API_KEY non configurée, validation ignorée")
return None
try:
response = await client.get(
f"{SYVEL_BASE_URL}/{email}",
headers={"Authorization": f"Bearer {SYVEL_API_KEY}"},
timeout=5.0,
)
if response.status_code == 200:
return SyvelResult(**response.json())
except httpx.TimeoutException:
logger.warning(f"Syvel timeout pour {email}")
except Exception as e:
logger.error(f"Syvel erreur inattendue : {e}")
return None
class RegisterRequest(BaseModel):
email: EmailStr
password: str
name: str
@router.post("/register", status_code=status.HTTP_201_CREATED)
async def register(body: RegisterRequest):
async with httpx.AsyncClient() as client:
quality = await check_email_quality(body.email, client)
if quality is not None:
# Bloquer les emails risqués (jetables, non livrables)
if quality.is_risky:
detail = {
"field": "email",
"code": f"email_{quality.reason}",
}
if quality.reason == "undeliverable":
detail["message"] = "Ce domaine ne peut pas recevoir d'emails."
elif quality.reason == "role_account":
detail["message"] = "Veuillez utiliser une adresse email personnelle (pas admin@, info@…)."
else:
detail["message"] = "Cette adresse email n'est pas acceptée."
# Suggérer une correction si typo détectée
if quality.did_you_mean:
detail["did_you_mean"] = quality.did_you_mean
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=detail,
)
# Ici : créer l'utilisateur en base, hasher le mot de passe, etc.
return {"status": "created", "email": body.email}

Bonnes pratiques FastAPI :

  • Réutilisez httpx.AsyncClient via un singleton ou l’injection de dépendances — créer un nouveau client par requête est inefficace.
  • Définissez un timeout court (5 s max) — un service tiers lent ne doit pas bloquer vos utilisateurs.
  • Loggez les erreurs mais ne bloquez pas — fail open est la règle pour les validations externes.

Partage du client HTTP avec le lifespan

app/main.py
from contextlib import asynccontextmanager
import httpx
from fastapi import FastAPI
http_client: httpx.AsyncClient | None = None
@asynccontextmanager
async def lifespan(app: FastAPI):
global http_client
http_client = httpx.AsyncClient(timeout=5.0)
yield
await http_client.aclose()
app = FastAPI(lifespan=lifespan)
# Dans votre router, injectez via dépendance :
from fastapi import Depends
def get_http_client() -> httpx.AsyncClient:
return http_client
@router.post("/register")
async def register(body: RegisterRequest, client: httpx.AsyncClient = Depends(get_http_client)):
quality = await check_email_quality(body.email, client)
...

Exemple 3 : Django

Pour Django, créez un validator personnalisé utilisable dans les modèles et formulaires :

myapp/validators.py
import httpx
import os
import logging
from django.core.exceptions import ValidationError
logger = logging.getLogger(__name__)
SYVEL_API_KEY = os.environ.get("SYVEL_API_KEY")
SYVEL_BASE_URL = "https://api.syvel.io/v1/check"
def validate_email_quality(email: str) -> None:
"""
Django validator — lève ValidationError si l'email est inacceptable.
Fail open en cas d'erreur externe.
"""
if not SYVEL_API_KEY:
return # Validation désactivée si clé non configurée
try:
with httpx.Client(timeout=5.0) as client:
response = client.get(
f"{SYVEL_BASE_URL}/{email}",
headers={"Authorization": f"Bearer {SYVEL_API_KEY}"},
)
if response.status_code != 200:
return # Fail open
data = response.json()
except Exception as e:
logger.error(f"Syvel validation error for {email}: {e}")
return # Fail open
if data.get("is_risky"):
reason = data.get("reason", "disposable")
if reason == "undeliverable":
raise ValidationError(
"Ce domaine ne peut pas recevoir d'emails.",
code="email_undeliverable",
)
elif reason == "role_account":
raise ValidationError(
"Veuillez utiliser une adresse email personnelle.",
code="email_role_account",
)
else:
raise ValidationError(
"Cette adresse email n'est pas acceptée. Veuillez utiliser votre email professionnel.",
code="email_disposable",
)
myapp/models.py
from django.db import models
from django.core.validators import validate_email
from .validators import validate_email_quality
class UserProfile(models.Model):
email = models.EmailField(
unique=True,
validators=[validate_email_quality], # S'applique à full_clean() et les formulaires
)
name = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True)
myapp/forms.py
from django import forms
from .validators import validate_email_quality
class RegistrationForm(forms.Form):
name = forms.CharField(max_length=255)
email = forms.EmailField(validators=[validate_email_quality])
password = forms.CharField(widget=forms.PasswordInput)

Exemple 4 : Traitement batch

Pour valider des milliers d’adresses (import de liste, purge CRM) :

batch_validate.py
import asyncio
import csv
import httpx
import os
import logging
from dataclasses import dataclass, asdict
from pathlib import Path
logger = logging.getLogger(__name__)
SYVEL_API_KEY = os.environ.get("SYVEL_API_KEY")
CONCURRENCY = 10 # Nombre de requêtes simultanées
@dataclass
class ValidationResult:
email: str
risk_score: int
is_risky: bool
reason: str
deliverability_score: int
did_you_mean: str
error: str = ""
async def validate_one(email: str, client: httpx.AsyncClient, semaphore: asyncio.Semaphore) -> ValidationResult:
async with semaphore:
try:
r = await client.get(
f"https://api.syvel.io/v1/check/{email}",
headers={"Authorization": f"Bearer {SYVEL_API_KEY}"},
timeout=10.0,
)
if r.status_code == 200:
d = r.json()
return ValidationResult(
email=email,
risk_score=d["risk_score"],
is_risky=d["is_risky"],
reason=d["reason"],
deliverability_score=d["deliverability_score"],
did_you_mean=d.get("did_you_mean") or "",
)
elif r.status_code == 429:
# Rate limit — attendre et réessayer
await asyncio.sleep(2)
return await validate_one(email, client, semaphore)
except Exception as e:
logger.error(f"Error validating {email}: {e}")
return ValidationResult(
email=email, risk_score=-1, is_risky=False,
reason="error", deliverability_score=-1,
did_you_mean="", error="API error",
)
async def validate_batch(emails: list[str]) -> list[ValidationResult]:
semaphore = asyncio.Semaphore(CONCURRENCY)
async with httpx.AsyncClient() as client:
tasks = [validate_one(email, client, semaphore) for email in emails]
results = await asyncio.gather(*tasks)
return list(results)
def main():
input_file = Path("emails.csv")
output_valid = Path("emails_valid.csv")
output_blocked = Path("emails_blocked.csv")
output_errors = Path("emails_errors.csv")
with open(input_file) as f:
reader = csv.DictReader(f)
emails = [row["email"].strip() for row in reader if row.get("email")]
print(f"🔍 Validation de {len(emails)} adresses...")
results = asyncio.run(validate_batch(emails))
valid = [r for r in results if not r.is_risky and r.risk_score >= 0]
blocked = [r for r in results if r.is_risky]
errors = [r for r in results if r.risk_score < 0]
# Écrire les résultats
fields = ["email", "risk_score", "is_risky", "reason", "deliverability_score", "did_you_mean", "error"]
for path, data in [(output_valid, valid), (output_blocked, blocked), (output_errors, errors)]:
with open(path, "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=fields)
writer.writeheader()
writer.writerows([asdict(r) for r in data])
print(f"✅ Valides : {len(valid)}")
print(f"🚫 Bloquées : {len(blocked)}")
print(f"⚠️ Erreurs : {len(errors)}")
print(f"\nFichiers générés : {output_valid}, {output_blocked}, {output_errors}")
if __name__ == "__main__":
main()

Utilisation :

Terminal window
# Format emails.csv : une colonne "email"
python batch_validate.py
# Validation de 5000 adresses...
# ✅ Valides : 4231
# 🚫 Bloquées : 623
# ⚠️ Erreurs : 146

Performance : avec CONCURRENCY = 10, 5 000 adresses sont traitées rapidement. Augmentez la concurrence selon votre plan Syvel (vérifiez votre quota de requêtes/minute dans le dashboard).


Résumé des patterns recommandés

ContextePatternPoints clés
Script CLIhttpx.Client synchroneSimple, direct
FastAPIhttpx.AsyncClient partagé via lifespanAsync, réutilisation client
DjangoValidator synchroneCompatible modèles + formulaires
Batchasyncio.gather + SemaphoreConcurrence contrôlée, gestion 429

Dans tous les cas : fail open (ne pas bloquer si l’API est indisponible), timeout court (5–10 s max), log des erreurs sans les propager à l’utilisateur.

Pour compléter votre stack, consultez aussi notre guide d’intégration Python dans la documentation officielle et le guide équivalent pour React / TypeScript. Pour comprendre quand utiliser chaque méthode de validation, lisez Regex, DNS, SMTP : quelle méthode choisir ?. Et pour les volumes importants, comparez les options tarifaires.

Protégez vos formulaires avec Syvel

Bloquez les emails jetables, catch-all et malformés en temps réel. API REST simple, conforme RGPD, hébergée en France.