How to Validate Emails in Python: A Go-to Guide for Software Engineers

On October 08, 2024
8min read
Ivan Djuric, an author at Mailtrap
Ivan Djuric Technical Content Writer @Mailtrap
Piotr Malek Technical Content Writer @ Mailtrap

In this tutorial, I break down the various approaches to Python email validation and show you how I typically go about it, code examples included.

P.S. I’ll also cover email verification by the end of the article, so, stick around or [jump ahead].

Validating emails with a regex

The most basic method I’ll show you is regex, or regular expressions. Python regex checks inserted email addresses for certain conditions and returns either a “valid” or “invalid” response.

To run basic regex checks, you can simply paste the following code snippet into your dedicated project file for validation (e.g., validate.py):

import re

mail1 = 'example@example.com'  # valid
mail2 = 'exa-mple@example.com'  # valid
mail3 = 'exa mple@example.com'  # not-valid (contains a space)
mail4 = 'example@example@example.com'  # not-valid (multiple @ symbols)
mail5 = 'example@examplecom'  # not-valid (missing '.' in domain part)

def validate(email):
    pattern = r'^[\w\.-]+@[a-zA-Z\d-]+\.[a-zA-Z]{2,}$'
    if re.match(pattern, email):
        print(f'{email} is correct')
    else:
        print(f'{email} is incorrect')

validate(mail1)
validate(mail2)
validate(mail3)
validate(mail4)
validate(mail5)

Explanation:

  • The mail1, mail2, etc., are email IDs I added so you can easily test whether your validation logic works.
  • The regex pattern in the validate function body is a string that represents an email and contains the criteria you want email addresses to meet.
  • The code checks addresses for basic email structure (e.g., correct formatting with one “@” and a valid domain).
  • We also use the re module for searching regex patterns and print for the valid/invalid messages.

Now, let me show you some examples of how you can play around with Python regex and adjust it according to your preferences. ⬇️

Example 1

The following regex checks if an email address is properly formatted and verifies if the top-level domain length is between two and four characters. You might want to raise the final number to a higher value to include top-level domains such as .travel, but that might also lead to some false positives.

^[\w.-]+@[\w.+-]+\.[a-zA-Z]{2,}

On the other hand, this script is not good for catching typos or fake email addresses such as !32d!3@dfsjn@dwef.com.

Example 2:

Let’s make it more sophisticated and assume we want the following criteria to be met for an account@domain email address:

  • Both the account and domain name include case-insensitive alphanumeric characters. Periods (.), dashes (-), and underscores (_) are also allowed.
  • Both must only start and end with alphanumeric characters. 
  • Neither the account nor domain may contain white spaces.
  • The account must have at least one character and the domain must have at least two.
  • The domain must include at least one period.
  • And of course, there must be an ‘@’ symbol separating the account and domain.
(^[A-Za-z0-9][A-Za-z0-9_.+-]+){1,}@[A-Za-z0-9_.-]+\.[A-Za-z]{2,}$

This is also a basic regex, and it will still capture some fake addresses. Although .com, .org, or .net domains need to be two or more characters long, many other top-level domains will allow just one character to be present. For example, Google’s Chinese domain, g.cn, would fail this test, but emails sent to big_brother@g.cn may just reach the target.

Above all, this regex completely ignores 99.9% of typos, making it ineffective as a standalone solution.

Example 3:

Since the other two examples did almost nothing about typos, you can also include another condition for popular email providers, such as Gmail:

import re
example = "example@gmial.com"
if re.search("@gm(ia|a|i)l.com$", example):
  print("Maybe you meant @gmail.com?")

If the domain name is gmial.com, gmal.com, or gmil.com (very likely indicating a Gmail account) then we can show a warning to the user. You can also expand this check for other popular email providers such as Yahoo and Outlook if you wish.

And, of course, to validate all possible typos, more sophisticated methods should be used, but listing the most common typos might just do the job.

Important: I must point out that however sophisticated regex you write, it will eventually fail for some legitimate email. You can read more about the possible trade-offs here. 🧐

Validating emails with Python libraries

A bit more advanced approach to Python email validation is to use libraries. I’ve had the most luck with the following two, and here’s how you can use them:

email-validator

email-validator is a Python library that not only focuses on the domain part of an email address, but also checks if it’s in an @x.com format. Moreover, it checks the basic email format, proper DNS records, internationalized domain names, and more.

As a bonus, the library also comes with a validation tool. It checks if the domain name can be resolved, thus giving you a good idea about its validity.

To install it, use the pip package installer and run the following command:

pip install email-validator

And here’s a code snippet you can use:

from email_validator import validate_email, EmailNotValidError

mail1 = 'example@example.com'  # valid
mail2 = 'exa-mple@example.com'  # valid
mail3 = 'exa mple@example.com'  # not-valid (contains a space)
mail4 = 'example@example@example.com'  # not-valid (multiple @ symbols)
mail5 = 'example@examplecom'  # not-valid (missing '.' in domain part)

def validate(email):
    try:
        email_info = validate_email(email)  # validate and get email info
        email = email_info.normalized  # validates the address and gives you its normalized form
        print(f'{email} is valid')  # print success message
    except EmailNotValidError as e:  # catch invalid emails
        print(f'{email} is not valid')  # print failure message
        print(str(e))  # print the specific error message

validate(mail1)
validate(mail2)
validate(mail3)
validate(mail4)
validate(mail5)

pyIsEmail

pylsEmail is another popular Python package that can be used to validate both a domain and an email address with just one call. If a given address can’t be validated, the script will inform you of the most likely reasons.

You can install it via pip with the following command:

$ pip install pyIsEmail

Then, simply copy the following code snippet into your validate.py file:

from pyisemail import is_email

mail1 = 'example@example.com'  # valid
mail2 = 'exa-mple@gmail.com'  # valid
mail3 = 'exa mple@gmail.com'  # not-valid
mail4 = 'example@example@example.com'  # not-valid
mail5 = 'example@gmail'  # not-valid

def validate(email):
    detailed_result = is_email(email, diagnose=True)
    print(detailed_result.description)

validate(mail1)
validate(mail2)
validate(mail3)
validate(mail4)
validate(mail5)

Validating emails with an external API

Finally, there are many APIs you can use with your Python app to validate email addresses. For your convenience, here’s a table comparing the features of the most popular ones:

Validation CheckMailValidation.ioMailboxlayerHunter.io APIZeroBounce API
Syntax 
MX Records
SMTP Server
Catch-All
Role-Based Email Detection
Typo Suggestions
Temporary Email Detection
Mailbox Full Detection
Spam Trap Detection
Blacklist Status
IP Address Validation

Pro tip: When choosing an API, I’d suggest you consider the level of email address validation your Python program needs, how easy to use it is, and whether it offers a solid price to feature ratio. Most importantly, ensure it’s being regularly updated and maintained, as you don’t want an outdated API.

If you’re wondering how any of these external APIs work, here’s a code snippet you can use to test the functionality of, for example, MailValidation.io:

import requests
from key import TEAM_SLUG, API_KEY

mail1 = 'example@gmail.com'  # not-valid
mail2 = 'test.mailtrap1234@gmail.com'  # valid

def validate(email):
    url = f"https://app.mailvalidation.io/a/{TEAM_SLUG}/validate/api/validate/"
    headers = {
        'content-type': 'application/json',
        'accept': 'application/json',
        'Authorization': 'Api-Key ' + API_KEY,
    }
    data = {'email': email}
    response = requests.post(
        url=url,
        headers=headers,
        json=data,
    )
    valid = response.json()['is_valid']
    if valid:
        print(f'{email} is correct')
    else:
        print(f'{email} is incorrect')

validate(mail1)
validate(mail2)

Keep in mind that you can also use MailValidation.io and the other APIs from the table to validate emails in bulk and emails from .txt, CSV, and other Excel file formats.

Bonus: email verification in Python

As you might have figured it out by now, email validation mostly focuses on making sure email addresses are entered correctly, without typos.

However, there’s also email verification, a process that goes a step further and not only scans for typos but also sends an activation link to the user’s email address, which they then have to activate. This way, you can actually make sure there’s someone behind the email address.

And here’s how I typically configure email verification in Python:

1. Setup your Python project

To verify emails in Python, you’ll first need to install the following libraries:

  • Flask – A lightweight Python web framework we’ll use for routing.
  • PyJWT – Library we’ll use to generate and validate JWT tokens.
pip install Flask PyJWT 

2. Generate tokens and send an email

Next, we’ll use the following code to generate an encoded token with expiration time exp and add email-sending functionality to our Python app via SMTP:

import smtplib
import jwt  # Correct import statement for PyJWT
from email.mime.text import MIMEText
from datetime import datetime, timedelta

# Configuration
port = 587
smtp_server = "smtp.mailtrap.io"  # Corrected SMTP
login = "your_mailtrap_username"  # Your login
password = "your_mailtrap_password"  # Your password
sender_email = "sender@example.com"
receiver_email = "newuser@example.com"

# Generate a JWT token for verification (valid for 30 minutes)
secret_key = 'your_secret_key'
token = jwt.encode(
    {"email": receiver_email, "exp": datetime.utcnow() + timedelta(minutes=30)},
    secret_key,
    algorithm="HS256"
)

# Email content with token embedded in verification URL
verification_link = f"http://localhost:5000/verify/{token}"
text = f"""\
Hi,

Please verify your email address exists by clicking on the link below:
{verification_link}

This link will expire in 30 minutes.
"""

# Create MIMEText object
message = MIMEText(text, "plain")
message["Subject"] = "Email Verification"
message["From"] = sender_email
message["To"] = receiver_email

# Send the email
with smtplib.SMTP(smtp_server, port) as server:
    server.starttls()  # Secure the connection
    server.login(login, password)
    server.sendmail(sender_email, receiver_email, message.as_string())

print('Email sent with verification token')

Tip: I typically add this code in a sendmail.py file I create for email-sending functionality.

3. Create a route to verify the token 

Then, we need to leverage Flask to create a route and verify the token. For this, copy the following code snippet into your main application file (e.g., app.py):

from flask import Flask, jsonify, request
import jwt

app = Flask(__name__)
secret_key = 'your_secret_key'

# Route to verify the token
@app.route('/verify/<token>', methods=['GET'])
def verify_token(token):
    try:
        # Decode the token
        decoded_token = jwt.decode(token, secret_key, algorithms=["HS256"])
        email = decoded_token["email"]
        return jsonify({"message": f"Email {email} has been successfully verified!"}), 200
    except jwt.ExpiredSignatureError:
        return jsonify({"error": "Verification link has expired."}), 400
    except jwt.InvalidTokenError:
        return jsonify({"error": "Invalid token."}), 400

if __name__ == "__main__":
    app.run(debug=True)

Next, run python sendmail.py (the first script) to send verification email to your email address. Once you click the link, the request will be handled by the flask server which you’ve just built.

And finally, try running your server and running python app.py to send yourself a test email. If everything’s working as intended, you should get an email message with a link you can click on to make a request on the server.

Email validation & verification in email testing

Confirming the correctness of an email address (validation) and the existence of a user behind it (verification) are just two of many aspects of an effective email testing strategy. 

Email testing is an industry-standard practice that ensures your sending code works flawlessly and that your emails successfully avoid spam filters, landing where they’re supposed to → recipients’ inboxes.

Consider it like car service before going on a road trip: adding a new exhaust or summer tires is cool, but without inspecting your vehicle beforehand, you don’t know where you’ll end up. 🚨

That’s why I always recommend using Mailtrap Email Testing, which lets you catch traffic from staging and dev environments and analyze the HTML/CSS of your emails. This way, you can remove or fix any faulty lines of code and ensure your messages are flawless before they reach your recipients.

What’s more, with the Spam Analysis feature, you get to see how likely your emails are to pass through spam filters. And by keeping the spam score under 5, you proactively boost your email deliverability

Lastly, besides offering a plethora of advanced features, Mailtrap Email Testing is super easy to use. You can learn how to test emails in Python by reading our step-by-step guide. Or, better yet if you’re a visual learner, follow along with the video we prepared for you! 👀

Further reading on email validation:

Ivan Djuric, an author at Mailtrap
Article by Ivan Djuric Technical Content Writer @Mailtrap

I’m a Technical Content Writer with 5 years of background covering email-related topics in tight collaboration with software engineers and email marketers. I just love to research and share actionable insights with you about email sending, testing, deliverability improvements, and more. Happy to be your guide in the world of emails!

Article by Piotr Malek Technical Content Writer @ Mailtrap