Comment vérifier des adresses e-mail en Python
Vérifier une adresse e-mail en Python est simple pour les parties faciles, et étonnamment difficile pour celle qui compte le plus. Ce guide présente les vérifications de syntaxe et les recherches DNS que vous pouvez exécuter en local, explique précisément pourquoi les sondes SMTP brutes échouent en pratique, et montre comment obtenir un résultat au niveau de la boîte mail avec une API REST.
Utilisez Python pour rejeter les saisies mal formées et, si besoin, mettre en cache les vérifications DNS/MX. Utilisez une API de vérification pour le résultat au niveau de la boîte mail, car les sondes smtplib brutes sont faciles à brider et renvoient souvent des réponses ambiguës.
Ce que "vérifier" un e-mail veut vraiment dire
La vérification d'emails consiste à confirmer qu'une adresse est réelle et peut recevoir des e-mails avant de lui envoyer quoi que ce soit. Elle se décompose en une suite de vérifications, chacune plus difficile que la précédente : l'adresse est-elle correctement formée, son domaine accepte-t-il des e-mails, et la boîte précise existe-t-elle ? En Python, les deux premières se font bien et facilement. La troisième est celle où la plupart des solutions maison échouent sans bruit, et comprendre pourquoi est l'élément le plus utile de cet article.
Le plan ci-dessous fonctionne par couches. Exécutez en local les vérifications rapides et peu coûteuses pour écarter les saisies manifestement erronées, puis appelez une API de vérification pour savoir, au niveau de la boîte mail, si l'adresse est utilisable. Vous rejetez ainsi instantanément les saisies aberrantes sans appel API, et obtenez un résultat plus clair pour tout ce qui passe ce premier filtre.
Étape 1 : validation de la syntaxe
La première vérification confirme que l'adresse est correctement formée. Il est tentant d'écrire une expression régulière pour cela, mais la syntaxe des e-mails est bien plus complexe qu'il n'y paraît, et les regex faites maison sont presque toujours trop strictes ou trop permissives. La bibliothèque de référence pour cette tâche est email-validator, qui implémente correctement les normes concernées et est largement utilisée en production.
Installez-la avec pip :
pip install email-validator
Puis validez une adresse. La bibliothèque lève EmailNotValidError pour toute adresse mal formée et renvoie un objet normalisé pour une saisie valide :
from email_validator import validate_email, EmailNotValidError
def check_syntax(address):
try:
result = validate_email(address, check_deliverability=False)
return True, result.normalized
except EmailNotValidError as error:
return False, str(error)
ok, value = check_syntax("jane.doe@example.com")
print(ok, value) # True jane.doe@example.com
ok, value = check_syntax("jane.doe@@example")
print(ok, value) # False, with a description of the problem
Définir check_deliverability=False limite cette étape à la syntaxe pure, sans aucun appel réseau : elle est donc assez rapide pour s'exécuter à chaque frappe ou à chaque envoi de formulaire. Stockez toujours la valeur normalized renvoyée par la bibliothèque plutôt que la saisie brute de l'utilisateur, car elle canonise l'adresse pour un stockage cohérent.
Étape 2 : vérifications du domaine et des MX
Une adresse correctement formée reste inutile si son domaine ne peut pas recevoir d'e-mails. La vérification suivante interroge les enregistrements DNS du domaine à la recherche d'enregistrements MX, qui désignent les serveurs de messagerie responsables de ce domaine. Un domaine sans enregistrement MX ne peut pas recevoir de courrier : toute adresse sur ce domaine peut donc être rejetée immédiatement.
Le email-validator peut s'en charger pour vous si vous passez check_deliverability=True, ce qui effectue la recherche MX pendant la validation :
from email_validator import validate_email, EmailNotValidError
def check_domain(address):
try:
result = validate_email(address, check_deliverability=True)
return True, result.domain
except EmailNotValidError as error:
return False, str(error)
Si vous voulez exécuter vous-même la requête DNS, par exemple pour mettre en cache les résultats de nombreuses adresses du même domaine, utilisez directement la bibliothèque dnspython pour cela :
pip install dnspython
import dns.exception
import dns.resolver
def has_mx_record(domain):
try:
records = dns.resolver.resolve(domain, "MX")
return len(records) > 0
except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN):
return False
except dns.exception.DNSException:
return False # Treat resolver errors as inconclusive.
print(has_mx_record("gmail.com")) # True
print(has_mx_record("no-such-domain-xyz.invalid")) # False
C'est vraiment utile. Cela élimine les adresses sur des domaines expirés, inexistants ou qui ne gèrent tout simplement pas le courrier. Mais remarquez ce que cette étape ne vous a toujours pas dit : si someone@gmail.com est une vraie boîte mail. Gmail possède des enregistrements MX, donc toutes les adresses Gmail passent cette vérification, valides ou non.
Étape 3 : la vérification de la boîte mail, et pourquoi le SMTP brut ne suffit pas
Confirmer la boîte elle-même exige de dialoguer avec le serveur de messagerie destinataire. En principe, vous vous connectez en SMTP, vous vous identifiez, envoyez une commande RCPT TO avec l'adresse cible et lisez le code de réponse : un code positif suggère que la boîte existe, un refus suggère qu'elle n'existe pas. Le module intégré de Python smtplib peut le faire, et le code paraît trompeusement simple :
import smtplib
import dns.resolver
def naive_smtp_check(address):
domain = address.split("@")[1]
mx_host = str(dns.resolver.resolve(domain, "MX")[0].exchange)
server = smtplib.SMTP(timeout=10)
server.connect(mx_host)
server.helo("example.com")
server.mail("probe@example.com")
code, message = server.rcpt(address)
server.quit()
# 250 and 251 generally mean the mailbox was accepted.
return code in (250, 251)
Cela fonctionne dans un tutoriel et échoue en production. Les raisons valent d'être connues en détail, car elles expliquent pourquoi aucun vérificateur sérieux ne s'appuie sur un script de ce type.
Réputation IP et bridage
Les fournisseurs de messagerie détectent activement les sondes de vérification. Lancez quelques vérifications RCPT TO depuis un seul serveur, et des fournisseurs comme Gmail et Outlook brideront ou bloqueront cette IP en quelques tentatives. Une fois bridé, le serveur renvoie des réponses ambiguës, que la boîte existe ou non, et vos résultats perdent tout sens. C'est précisément pour cette raison que la vraie vérification s'effectue depuis de grands pools d'adresses IP correctement préchauffées et bien gérées.
Greylisting
De nombreux serveurs pratiquent le greylisting, en renvoyant volontairement un échec temporaire 4xx la première fois qu'un expéditeur inconnu prend contact. Les serveurs de messagerie légitimes réessaient plus tard ; les spammeurs, en général, non. Un script naïf qui ne réessaie pas interprète cet échec temporaire comme un refus et marque comme invalide une boîte parfaitement valide. C'est un faux négatif dû uniquement à l'impatience.
Domaines catch-all
Un domaine catch-all accepte le courrier pour toutes les adresses possibles, donc RCPT TO renvoie un code positif pour realperson@domain.com et le random-string-9f3a@domain.com sans distinction. Une vérification SMTP brute ne peut pas distinguer une vraie boîte d'une boîte inexistante sur ces domaines. Détecter un catch-all exige d'envoyer une seconde sonde vers une adresse qui n'existe certainement pas et de voir si elle est acceptée aussi : c'est de la détection catch-all, pas un simple oui ou non.
Réponses ambiguës des grands fournisseurs
Chez certains grands fournisseurs, la réponse à RCPT TO est délibérément ambiguë. Certains acceptent toutes les adresses au stade de la poignée de main et ne génèrent le rebond que plus tard : la réponse SMTP ne contient tout simplement pas l'information que vous cherchez.
C'est pourquoi les mainteneurs de email-validator excluent délibérément les vérifications SMTP de la bibliothèque. Leur documentation indique clairement qu'il n'y a rien à gagner à contacter soi-même un serveur SMTP, car les serveurs savent très bien ne pas révéler si une adresse est délivrable, et une adresse qui semble accepter le courrier peut tout de même rebondir ensuite. La conclusion honnête : la syntaxe et les MX, vous pouvez bien les gérer dans votre propre code, mais pas une vérification fiable de la boîte mail.
Étape 4 : vérifier avec l'API VeriMails
La solution pratique consiste à confier la vérification de la boîte mail à un service de vérification, depuis une infrastructure conçue pour cela, et à appeler ce service depuis Python avec une seule requête HTTP. VeriMails propose une API REST qui exécute toute la chaîne : syntaxe, MX, DNS, poignée de main SMTP en direct, détection des catch-all, détection des adresses jetables et génériques, et renvoie un résultat structuré unique.
Avec la bibliothèque standard requests , un appel de vérification ressemble à ceci. Pour le détail du point de terminaison, consultez la Documentation de l'API VeriMails:
import requests
def verify_email(address, api_key):
response = requests.get(
"https://api.verimails.com/v1/verify",
params={"email": address},
headers={"Authorization": f"Bearer {api_key}"},
timeout=10,
)
response.raise_for_status()
return response.json()
result = verify_email("jane.doe@example.com", api_key="YOUR_API_KEY")
print(result["status"]) # e.g. "deliverable", "undeliverable", "catch_all"
print(result["disposable"]) # True if a throwaway address
print(result["role_based"]) # True for addresses such as info@ or support@
Vous pouvez ensuite agir selon le résultat. Un formulaire d'inscription peut accepter les adresses deliverable , rejeter les adresses undeliverable et le disposable et définir sa propre politique pour les résultats catch_all:
def is_acceptable_for_signup(address, api_key):
result = verify_email(address, api_key)
if result["disposable"]:
return False, "Disposable addresses are not allowed."
if result["status"] == "undeliverable":
return False, "That address does not appear to exist."
return True, "Address verified."
Comme chaque appel renvoie un résultat clair, il est assez rapide pour être exécuté pendant l'envoi d'un formulaire sans faire attendre l'utilisateur. Pour une liste d'adresses déjà présente dans votre base, vérification CSV en masse est plus adaptée, car importer un seul fichier est bien plus efficace que d'appeler le point de terminaison unitaire des milliers de fois en boucle.
Le schéma recommandé pour une application Python combine les deux couches : conservez la vérification de syntaxe locale avec email-validator pour rejeter instantanément les saisies aberrantes avant de dépenser un crédit, puis appelez l'API pour obtenir le résultat de délivrabilité de référence sur tout ce qui passe. Vous obtenez un retour rapide sur les erreurs évidentes et un verdict fiable pour les vraies adresses.
Tableau des politiques applicatives
| Résultat | Politique à l'inscription | Politique d'import ou CRM |
|---|---|---|
deliverable | Accepter et stocker l'adresse normalisée | Conserver dans le segment actif |
undeliverable | Rejeter avec un message de correction clair | Exclure avant toute campagne |
catch_all | Accepter ou examiner selon le risque du compte | Segmenter à part et envoyer avec prudence |
disposable | Rejeter pour la plupart des produits | Exclure des listes de prospection |
role_based | Autoriser uniquement si les boîtes partagées sont acceptables | Examiner avant la prospection à froid |
Appels API ou traitements de listes
Utilisez l'API là où une décision immédiate compte : inscription, paiement, formulaires de leads, invitations et modifications de compte. Utilisez la vérification en masse pour un export CSV, un nettoyage de CRM ou une audience de campagne où des milliers d'adresses doivent passer par la même revue. Les deux workflows doivent alimenter les mêmes règles d'exclusion, pour qu'une adresse rejetée par un canal ne soit pas contactée plus tard depuis un autre outil.
Tout assembler
Une implémentation propre comporte trois couches. D'abord, une email-validator vérification de syntaxe avec check_deliverability=False, exécutée localement et instantanément pour rejeter les saisies mal formées. Ensuite, une vérification MX optionnelle avec dnspython si vous voulez filtrer les domaines morts avant d'appeler l'API, même si l'API couvre aussi ce point. Enfin, un appel à l'API VeriMails pour le résultat au niveau de la boîte mail, y compris la détection des catch-all, des adresses jetables et génériques, que vous ne pouvez pas reproduire de façon fiable par vous-même.
Ce qu'il faut retenir, c'est la répartition des rôles. La syntaxe et le DNS, vous pouvez vraiment les gérer en Python. La vérification de la boîte en direct dépend de la réputation IP, du comportement de greylisting et de la détection catch-all, qu'un script isolé ne peut pas maîtriser : c'est pourquoi la bibliothèque de validation de référence exclut délibérément les sondes SMTP. Utilisez les vérifications locales pour la rapidité, une API pour la vérification au niveau de la boîte mail, et les page des tarifs pour choisir la formule adaptée à votre volume prévu.
Questions fréquentes
Articles liés
Partez de données propres
100 crédits gratuits à l'inscription. Sans carte bancaire. Mettez ces conseils en pratique dès aujourd'hui.
Commencer gratuitement