How to validate an email address in Ruby

Last Updated Dec 29, 2020
Emma Jagger

Engineer, maker, Google alumna, CMU grad

If you use email marketing for your business or send emails to mailing lists, you are probably familiar with the challenge of having bad or fake email addresses in your database.

Every time you send a bad email, you receive a notification that your message has been rejected, which is more than just an annoyance and represents a significant risk.

The biggest risk: having too many fake emails in your mailing list

Service providers check your bounce rate. If they find that you are sending too many emails to the wrong addresses, they may blacklist you. This can have even greater implications than spam filtering, as many of your recipients' mail servers could reject every single mail you send. Once your servers are blacklisted, it can be complicated to remove the blacklisting.

You can verify your server's blacklisting status on a service like Debouncer.

How to validate an email address in Ruby

As a developer, you are looking to implement the best way to validate an email address format. To do so, you first need to understand precisely what format is allowed by Internet Standards.

Section 3,2,4 of RFC 2822 specifies that the local string (the part of the email address that comes before the @) can contain the following characters: ! $ & * - = ` ^ | ~ # % ' + / ? _ { }

According to this, validation of an email address format requires quite a complex mechanism. Luckily the URI::MailTo class, which is part of the Ruby Standard Library, contains the URI::MailTo::EMAIL_REGEXP constant, which can be used for email format validation, so you don't have to create your own.


"mymail@myserver.com" =~ URI::MailTo::EMAIL_REGEXP
=> 0 # Successful validation

"wrong" =~ URI::MailTo::EMAIL_REGEXP
=> nil # Not a valid email address

However, according to the next section of RFC 2822, an email address can contain any characters as long as quotes escape them. As an example, this is a correct email address:


"Some spaces! And @ sign too!" @some.server.com

And the URI::MailTo::EMAIL_REGEXP constant would reject it.

At this point, you could conclude that URI::MailTo::EMAIL_REGEXP is able to validate 99% of all emails submitted to your forms, which may be good enough.

But an address like sefgqpouinqlzoiyhqjhzgf@gmail.com has a correct syntax and would be validated by any regular expression you may implement. Chances are, though, that the mailbox does not exist, and every email sent to this address would bounce back.

APIs: The real solution to validating email addresses

An efficient email checker would not only verify the address format but also verify if the email is hosted by a free or paid service, if the domain correctly contains an MX record and if it points to a valid SMTP server, if the email domain is linked to a disposable email service, and more.

Abstract is a fast and reliable API provider that offers multiple services, including email validation. Once subscribed, you get free access to the API along with your private API key.

Abstract Email Validation API is accessible through a GET request and takes 2 arguments. Here is its format:

c

uri = "https://emailvalidation.abstractapi.com/v1/?api_key=#{api_key}&email=#{amail_eddress}"

Using Net::HTTP, here is how to make the request and retrieve the response:


require 'net/http'
require 'net/https'

api_key = "YOUR_API_KEY"

def make_abstract_request(email_address)
  uri = URI("https://emailvalidation.abstractapi.com/v1/?api_key=#{api_key}&email=#{email_address}")

  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true
  http.verify_mode = OpenSSL::SSL::VERIFY_PEER

  request =  Net::HTTP::Get.new(uri)

  response = http.request(request)

  return response.body
rescue StandardError => error
  puts "Error (#{ error.message })"
end

make_abstract_request("email@to.validate")

The response is in JSON format and contains multiple parameters:

  • auto_correct: contains a suggestion of the correct email if a typo error has been detected
  • is_valid_format: check the email format and indicates if it is correct
  • is_free_email: indicates if the email is hosted by a free service (Gmail, Outlook, ...)
  • is_disposable_email: indicates if the email address is provided by a disposable email provider, which is a service allowing people to receive email without any authentication nor identification
  • is_role_email: indicates if the email recipient seems to be a team or a department instead of a person
  • is_catchall_email: indicates if the email lands in the catchall inbox of the SMTP server
  • is_mx_found (only available on paid plans): indicates if the domain provides one or more MX records and if they point to a server
  • is_smtp_valid (only available on paid plans): check the SMTP domain and indicate if the verification was successful
  • quality_score: a number scaling from 0.01 to 0.99, indicating the level of trust of the email address submitted

Here is an example of a response:


{
  "email": "johnsmith@gmail.com",
  "autocorrect": "",
  "is_valid_format": {
        "value": true,
        "text": "TRUE"
        },
  "is_free_email": {
        "value": true,
        "text": "TRUE"
        },
  "is_disposable_email": {
        "value": false,
        "text": "FALSE"
        },
  "is_role_email": {
        "value": false,
        "text": "FALSE"
        },
  "is_catchall_email": {
        "value": false,
        "text": "FALSE"
        },
  "is_mx_found": {
        "value": true,
        "text": "TRUE"
        },
  "is_smtp_valid": {
        "value": true,
        "text": "TRUE"
        },
  "quality_score": 0.90,
}

Related articles