E-Mail-Adressen in Python verifizieren

Eine E-Mail-Adresse in Python zu verifizieren ist bei den einfachen Teilen unkompliziert und bei dem Teil, der am meisten zählt, überraschend schwer. Dieser Guide zeigt Syntaxprüfungen und DNS-Lookups, die du lokal ausführen kannst, erklärt genau, warum rohe SMTP-Abfragen in der Praxis scheitern, und zeigt, wie du über eine REST-API ein Ergebnis auf Postfach-Ebene bekommst.

Kurz gesagt

Nutze Python, um fehlerhafte Eingaben abzulehnen und optional DNS-/MX-Prüfungen zu cachen. Für das Ergebnis auf Postfach-Ebene nutzt du eine Verifizierungs-API, weil direkte Abfragen per smtplib leicht gedrosselt werden und oft mehrdeutige Antworten liefern.

Was „Verifizieren“ einer E-Mail eigentlich bedeutet

E-Mail-Verifizierung ist der Prozess, mit dem du bestätigst, dass eine Adresse echt ist und Mails empfangen kann, bevor du irgendetwas an sie sendest. Er besteht aus einer Reihe von Prüfungen, jede schwieriger als die vorherige: Ist die Adresse korrekt formatiert, nimmt ihre Domain überhaupt Mails an, und existiert das konkrete Postfach? In Python lassen sich die ersten beiden gut umsetzen. Beim dritten scheitern die meisten Eigenbau-Lösungen still und leise, und zu verstehen, warum, ist das Nützlichste an diesem Artikel.

Der Plan unten ist mehrstufig. Führe die günstigen, schnellen Prüfungen lokal aus, um offensichtlich kaputte Eingaben auszusortieren, und ruf dann eine Verifizierungs-API für die Antwort auf Postfach-Ebene auf, ob die Adresse nutzbar ist. So lehnst du Müll sofort ohne API-Aufruf ab und bekommst für alles, was übrig bleibt, ein klareres Ergebnis.

Mehrstufige Email-Verifizierung in Python mit Syntaxprüfung, DNS-MX-Abfrage, API-Verifizierung und App-Richtlinie
Eine zuverlässige Python-Implementierung hält lokale Prüfungen schnell und günstig und holt sich die Ergebnisse zu SMTP, Catch-all, Wegwerf- und Rollen-Adressen von einer verwalteten Verifizierungs-API.

Schritt 1: Syntaxvalidierung

Die erste Prüfung bestätigt, dass die Adresse korrekt aufgebaut ist. Es ist verlockend, dafür einen regulären Ausdruck zu schreiben, aber die E-Mail-Syntax ist viel komplizierter, als sie aussieht, und selbstgebaute Regexes sind fast immer entweder zu streng oder zu lasch. Die Referenz-Bibliothek dafür ist email-validator. Sie setzt die relevanten Standards sauber um und ist in Produktion weit verbreitet.

Installiere sie mit pip:

pip install email-validator

Dann validierst du eine Adresse. Die Bibliothek wirft EmailNotValidError bei allem, was fehlerhaft ist, und gibt bei gültigen Eingaben ein normalisiertes Objekt zurück:

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

Mit check_deliverability=False bleibt dieser Schritt eine reine Syntaxprüfung ohne Netzwerkaufrufe und ist damit schnell genug für jeden Tastendruck oder jedes abgeschickte Formular. Speichere immer den Wert normalized , den die Bibliothek zurückgibt, statt der rohen Nutzereingabe, denn sie bringt die Adresse für eine konsistente Speicherung in eine kanonische Form.

Schritt 2: Domain- und MX-Prüfungen

Eine korrekt formatierte Adresse ist trotzdem nutzlos, wenn ihre Domain keine E-Mails empfangen kann. Die nächste Prüfung fragt die DNS-Records der Domain ab und sucht nach MX-Records, die die zuständigen Mailserver der Domain benennen. Eine Domain ohne MX-Records kann keine Mails annehmen, also lässt sich jede Adresse darauf sofort ablehnen.

Die email-validator kann das für dich erledigen, wenn du check_deliverability=Trueübergibst. Dann läuft der MX-Lookup direkt als Teil der Validierung mit:

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)

Wenn du die DNS-Abfrage selbst ausführen willst, etwa um Ergebnisse für viele Adressen auf derselben Domain zu cachen, nutze die Bibliothek dnspython direkt:

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

Das ist wirklich nützlich. Es sortiert Adressen auf Domains aus, die abgelaufen sind, nie existiert haben oder schlicht keine Mails verarbeiten. Aber beachte, was es dir immer noch nicht verrät: ob someone@gmail.com ein echtes Postfach ist. Gmail hat MX-Records, also besteht jede Gmail-Adresse diese Prüfung, ob gültig oder nicht.

Schritt 3: Die Postfach-Prüfung und warum rohes SMTP nicht reicht

Um das Postfach selbst zu bestätigen, musst du mit dem empfangenden Mailserver sprechen. Im Prinzip verbindest du dich per SMTP, stellst dich vor, sendest den Befehl RCPT TO mit der Zieladresse und liest den Antwortcode: Ein positiver Code deutet darauf hin, dass das Postfach existiert, eine Ablehnung darauf, dass es nicht existiert. Pythons eingebautes smtplib kann das, und der Code sieht trügerisch einfach aus:

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)

Im Tutorial funktioniert das, in Produktion scheitert es. Die Gründe lohnen sich im Detail, denn sie erklären, warum sich kein ernsthafter Verifizierer auf ein Skript wie dieses verlässt.

IP-Reputation und Drosselung

Mailbox-Anbieter erkennen Verifizierungsabfragen aktiv. Führe von einem Server aus eine Handvoll Prüfungen per RCPT TO durch, und Anbieter wie Gmail und Outlook drosseln oder blockieren diese IP nach wenigen Versuchen. Ist die IP erst gedrosselt, liefert der Server mehrdeutige Antworten, egal ob das Postfach existiert, und deine Ergebnisse werden bedeutungslos. Echte Verifizierung läuft genau deshalb über große Pools sauber aufgewärmter, gut verwalteter IP-Adressen.

Greylisting

Viele Server nutzen Greylisting und geben absichtlich einen vorübergehenden Fehler 4xx zurück, wenn ein unbekannter Absender zum ersten Mal Kontakt aufnimmt. Legitime Mailserver versuchen es später erneut, Spammer meist nicht. Ein naives Skript ohne Wiederholung liest diesen vorübergehenden Fehler als Ablehnung und markiert ein völlig intaktes Postfach als ungültig. Das ist ein falsch negatives Ergebnis, verursacht allein durch Ungeduld.

Catch-all-Domains

Eine Catch-all-Domain akzeptiert Mails an jede mögliche Adresse, daher liefert RCPT TO einen positiven Code für realperson@domain.com und im random-string-9f3a@domain.com gleichermaßen. Eine rohe SMTP-Prüfung kann auf diesen Domains ein echtes Postfach nicht von einem nicht existierenden unterscheiden. Um ein Catch-all zu erkennen, musst du eine zweite Abfrage an eine Adresse schicken, die ganz sicher nicht existiert, und schauen, ob auch sie akzeptiert wird. Das ist Catch-all-Erkennung, kein einfaches Ja oder Nein.

Mehrdeutige Antworten großer Anbieter

Einige große Anbieter haben ihr Verhalten bei RCPT TO bewusst mehrdeutig gemacht. Manche akzeptieren im Handshake jede Adresse und bouncen erst später, sodass die SMTP-Antwort die gesuchte Information schlicht nicht enthält.

Deshalb lassen die Maintainer von email-validator SMTP-Prüfungen bewusst aus der Bibliothek heraus. Ihre Dokumentation sagt klar, dass es nichts bringt, selbst einen SMTP-Server zu kontaktieren, weil Server gut darin sind, nicht preiszugeben, ob eine Adresse zustellbar ist, und weil eine Adresse, die Mails anzunehmen scheint, danach trotzdem bouncen kann. Die ehrliche Erkenntnis: Syntax- und MX-Prüfungen kannst du in deinem eigenen Code gut umsetzen, eine zuverlässige Postfach-Prüfung nicht.

Schritt 4: Verifizieren mit der VeriMails-API

Die praktische Lösung: Überlass die Postfach-Prüfung einem Verifizierungsdienst mit dafür gebauter Infrastruktur und ruf diesen Dienst aus Python mit einem einzigen HTTP-Request auf. VeriMails bietet eine REST-API , die die komplette Pipeline ausführt: Syntax, MX, DNS, einen Live-SMTP-Handshake, Catch-all-Erkennung, Wegwerf-Erkennung und Rollen-Erkennung, und ein strukturiertes Ergebnis zurückgibt.

Mit der Standard-Bibliothek requests sieht ein Verifizierungsaufruf so aus. Details zum Endpunkt findest du in der VeriMails API-Dokumentation:

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@

Danach kannst du auf das Ergebnis reagieren. Ein Anmeldeformular könnte Adressen mit deliverable akzeptieren, solche mit undeliverable und im disposable ablehnen und für 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."

eine eigene Regel festlegen. Da jeder Aufruf ein klares Ergebnis liefert, ist er schnell genug für den Einsatz beim Absenden eines Formulars, ohne dass die Person warten muss. Für eine bestehende Liste von Adressen in deiner Datenbank ist die Massen-CSV-Verifizierung die bessere Wahl, denn eine einzige Datei hochzuladen ist weit effizienter, als den Endpunkt für einzelne Adressen tausende Male in einer Schleife aufzurufen.

Das empfohlene Muster für eine Python-Anwendung kombiniert beide Ebenen: Behalte die lokale Syntaxprüfung mit email-validator bei, um Müll-Eingaben sofort abzulehnen, bevor du einen Credit ausgibst, und ruf dann für alles, was durchkommt, die API für das maßgebliche Zustellbarkeitsergebnis auf. So bekommst du schnelles Feedback bei offensichtlichen Fehlern und ein verlässliches Urteil für echte Adressen.

Tabelle: Regeln für die Anwendung

ErgebnisRegel bei der RegistrierungRegel bei Import oder CRM
deliverableNormalisierte Adresse akzeptieren und speichernIm aktiven Segment behalten
undeliverableMit klarem Korrekturhinweis ablehnenVor jeder Kampagne unterdrücken
catch_allJe nach Kontorisiko akzeptieren oder prüfenSeparat segmentieren und vorsichtig senden
disposableFür die meisten Produkte ablehnenAus Outreach-Listen unterdrücken
role_basedNur zulassen, wenn geteilte Postfächer akzeptabel sindVor Cold Outreach prüfen

API-Aufrufe vs. Listen-Jobs

Nutze die API dort, wo eine sofortige Entscheidung zählt: bei Registrierung, Checkout, Lead-Formularen, Einladungen und Kontoänderungen. Nutze die Massenverifizierung für einen CSV-Export, eine CRM-Bereinigung oder eine Kampagnen-Zielgruppe, bei der tausende Adressen dieselbe Prüfung brauchen. Beide Workflows sollten dieselben Unterdrückungsregeln speisen, damit eine Adresse, die auf einem Weg abgelehnt wurde, später nicht über ein anderes Tool angeschrieben wird.

Alles zusammengesetzt

Eine saubere Implementierung hat drei Ebenen. Erstens eine Syntaxprüfung mit email-validator und check_deliverability=False, lokal und sofort ausgeführt, um fehlerhafte Eingaben abzulehnen. Zweitens eine optionale MX-Prüfung mit dnspython , wenn du tote Domains vor dem API-Aufruf herausfiltern willst, auch wenn die API das ebenfalls abdeckt. Drittens ein Aufruf der VeriMails-API für das Ergebnis auf Postfach-Ebene, inklusive Catch-all-, Wegwerf- und Rollen-Erkennung, die du allein nicht zuverlässig nachbauen kannst.

Wichtig ist die Arbeitsteilung. Syntax und DNS kannst du wirklich selbst in Python erledigen. Die Live-Postfach-Prüfung hängt von IP-Reputation, Greylisting-Verhalten und Catch-all-Erkennung ab, die ein einzelnes Skript nicht stemmen kann. Genau deshalb lässt die Standard-Validierungsbibliothek SMTP-Abfragen bewusst weg. Nutze lokale Prüfungen für Tempo, eine API für die Verifizierung auf Postfach-Ebene und die Preisseite , um den Plan zu wählen, der zu deinem erwarteten Volumen passt.

Häufig gestellte Fragen

Mit der Bibliothek email-validator kannst du die Syntax prüfen und mit dnspython bestätigen, dass eine Domain MX-Records hat, beides komplett in deinem Code. Was du allein nicht zuverlässig kannst: bestätigen, dass das Postfach existiert. Dafür braucht es einen Live-SMTP-Handshake von vertrauenswürdigen IP-Adressen, und rohe smtplib-Abfragen von einem einzelnen Server werden von Mailbox-Anbietern meist blockiert oder gedrosselt.
Große Anbieter erkennen Verifizierungsabfragen und drosseln die Quell-IP nach wenigen Versuchen. Danach liefern sie mehrdeutige Antworten, egal ob das Postfach existiert. Greylisting gibt unbekannten Absendern einen vorübergehenden Fehler zurück, den ein naives Skript als ungültig fehldeutet. Catch-all-Domains akzeptieren jede Adresse. Aus genau diesen Gründen lässt die offizielle Bibliothek email-validator SMTP bewusst weg.
Mit einem mehrstufigen Ansatz: Führe lokal eine schnelle Syntaxprüfung aus, um offensichtlich kaputte Eingaben abzulehnen, und ruf dann eine Verifizierungs-API wie VeriMails für das Ergebnis auf Postfach-Ebene auf. Die API führt den SMTP-Handshake über sauber verwaltete Infrastruktur durch und liefert Syntax-, MX-, DNS-, SMTP-, Catch-all-, Wegwerf- und Rollen-Ergebnisse in einer Antwort.
Die VeriMails-API liefert für jede Adresse ein klares Ergebnis und ist schnell genug für den Aufruf beim Absenden eines Formulars. Neue Konten bekommen 100 kostenlose Credits ohne Kreditkarte, sodass du die Integration testen kannst, bevor du etwas bezahlst.

Starte mit sauberen Daten

100 Gratis-Credits bei der Anmeldung. Keine Kreditkarte nötig. Setz den Rat noch heute in die Praxis um.

Kostenlos starten
Keine Kreditkarte nötig. Credits verfallen nie.