Build your own Email Verification Tool.

How to build your own simple Email Verification Tool.

👋 Introduction to Email Verification

Hey everyone,

This is a quick guide on how to verify emails (for free). 

After following the tutorial, you’ll be able to add email addresses into Google Sheets and check if they are valid and/or catch-all email addresses.

Email verification is important to ensure bounce rates < 5% and Spam < 1% (these are metrics that demonstrate you are operating with integrity when sending emails. Operating inside these parameters means you can operate scale).

This tutorial explains the initial MVP that I built. Using it reduced bounce from 30% on average to ranging from 3.6% – 12%. It’s hacked together and done super-quick but it works 🙂

To give first to other Founders, I am sharing how I built this MVP that we used internally @ Drag (main business). You can build and run it locally or simply use npm modules/logic to programmatically build into your workflow (for free)

Since this MVP, I’ve spun out Row (for free again) to help Founders run email verification at a larger scale. Row is a Google Sheets Add-on where you paste unlimited email addresses into a column and the tool will verify emails row-by-row, super-fast.

Row has had lots of extra work since this tutorial / MVP due to scaling up @ Drag. It’s now even more accurate, and truly scalable to verify in bulk. If you are a business and need something commercially ready, give it a try

Good luck building.

Thanks

Nick Timms (here’s me)

Onto the tutorial…

Next step: Set up your environment

🌎 Step 1: Set up your environment

Node

I will be building this Email Verification Tool in Node. It is an open-source, cross-platform JavaScript run-time environment that executes JavaScript code outside the browser. The most important advantage of using Node is that we can use JavaScript as both a front-end and back-end language.

For this, we’ll only be using it on the back-end. The Google Sheets integration will act as our front-end interface for users.

You can download Node here.

npm i module_name

Installed modules can be used using the require() function:

var module = require(‘module_name’)

I will be using a package.json file for the module versions. This can be created with the command:

npm init

There are lots of frameworks and modules that we can use. To make things easier, we’ll use some email-verification modules to make this tutorial really simple.

Google Sheets

I will be using Google Sheets to request email records and to return the results of the email verification.

You can find instructions on how to use Google Sheets API here. You will need a Google Workspace (previously G Suite) account to start using the APIs, and I will also use another npm module to make accessing the API easier.

Next step: Start building

💪 Step 2: Start building

We can start by creating a new project directory and moving into it.

Then we can initiate our project with the following command:

npm init

This will create a package.json file where you add the information on our project (you will be prompted to answer questions in the terminal). The final results will look something like this:


{
  "name": "automationmachine",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Nick Timms",
  "license": "ISC",
  "dependencies": {
    "csv-writer": "^1.6.0",
    "google-spreadsheet": "^3.0.13",
    "email-verify": "^1.2",
    "request": "^2.88.2",
    "request-promise": "^4.2.6"
  }
}
Next step: Build MX Handshake

🤝 Step 3: Build MX Handshake

Next, we’ll use an npm module called email-verify. There are a few options, each uses SMTP email verification to connect to a remote email server, establish a connection, and ultimately validate or invalidate an email.

There are a few others you may want to look at. Notable alternatives to email-verify:
email-deep-validator
email-validator

Install email-verify with the following command:

npm install -g email-verify

Next, we’ll create a file called pingServer.js to run the MX Handshake. MX Handshake is a common term used to explain connecting with an SMTP server to get a response. 

In this file, we need to require email-verify:

var verifier = require("email-verify")

We can then create a function that takes a value of email and uses email-verify to check if the email is valid. Check if it’s working by commenting out the module.export and temporarily calling the function:

verifyemail("name@company.com")

The final pingServer.js code will look something like this:


// This script performs an MX handshake and 6 other checks to check IF email is valid. 

var verifier = require("email-verify");
var infoCodes = verifier.verifyCodes;
// const grabSheetsData = require('./googlesheetsbatch');

function verifyemail(email) {
  return new Promise((resolve, reject) => {
    verifier.verify(email, function(err, info) {
      try {
        if (err) console.log(err);
        else {
          return resolve({
            // Email address value
            email: email,
            // Valid email?
            emailinfo: info.info,
            // Successful connection
            success: info.success,
            // Connected to SMTP server and finished email verification
            finishedverification: info.code === infoCodes.finishedVerification,
            // Domain not found
            domainnotfound: info.code === infoCodes.domainNotFound,
            // Email sstructure not valid
            invalidemailstructure:
              info.code === infoCodes.invalidEmailStructure,
            // No MX records
            nomxrecords: info.code === infoCodes.noMxRecords,
            //SMTP connection timeout
            smtpconnectiontimeout:
              info.code === infoCodes.SMTPConnectionTimeout,
            // SMTP connection error
            smtpconnectionerror: info.code === infoCodes.SMTPConnectionError
          });
        }
      } catch (error) {
        console.log("Error", error);
        return resolve(error);
      }
    });
  });
}

module.exports = verifyemail;

It takes any email as an argument and completes the following checks on the email:

1. Validate it is a proper email address
2.Get the domain of the email
3. Grab the DNS MX records for that domain
4. Create a TCP connection to the SMTP server
5. Send a HELO message
6. Send a MAIL FROM message
7. Send a RCPT TO message
8. If they all validate, return an object with success: true. If any stage fails, the callback object will have success: false.

The response looks like this:


email: 'nick@dragapp.com',
emailInfo: 'nick@dragapp.com is a valid address',
success: true,
finishedVerification: true,
domainNotFound: false,
invalidEmailStructure: false,
noMxRecords: false,
smtpConnectionTimeout: false,
smtpConnectionError: false
isCatchAll: false

We now have a way to check if an email is valid by verifying it using this function. Now time to automate the process and build it into a useful UI (Google Sheets 🎉).

Next step: Connect to Google Sheets

📝 Step 4: Connect to Google Sheets

We now have a fully working script to check if any email is valid. Next, we are going to access Google Sheets and pass a list of emails, verify each email, and write the results into the same sheet. Here goes 🙂

Create a new file called emailVerifier.js and require two things. Firstly, we’ll require the pingServer file that we just wrote. Secondly, we’re going to use another npm module called google-spreadsheet to simplify using Google Sheets API.

const verifyemail = require("./pingServer")

const { GoogleSpreadsheet } = require("google-spreadsheet")

We’ll add two variables. They will tell the app the start row (row) and end row (endrow) therefore providing a list of emails to verify. It is useful to set parameters like this to begin – it will help to start small and understand the point of failure before you scale out the email verification process.

const row = 0;
const endrow = 110;

Create a configuration file called automation-machine.json and require it. Inside the file you will store all of your private information to access Google Sheets. Follow these instructions to fill out the below information.


{
  "type": "service_account",
  "project_id": "automation-machine",
  "private_key_id": "",
  "private_key": "",
  "client_email": "",
  "client_id": "",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": ""
}

Connect to Google Sheets and create an async function called getCsvData to access Sheets data:


const doc = new GoogleSpreadsheet(
  "1jvho9dOfP2_HBrfvPCbQF8_OEToCi_9DQOW3Lnm4ikA"
);

async function getCsvData() {
  await doc.useServiceAccountAuth(credentials);
  await doc.loadInfo();
}

We’ll create an async function called emailvalue. It accesses a single row and calls the emailverify function (we created this in the previous section). Note that we are doing two key things here:

1. Check if email is valid
This will explicitly check if the given email is valid based on the npm module that we are using.

2. Check if email is catch-all
We do this by adding a random string before the ‘@’. If nick@ is true and randomnick@ is also true then it invalidates the first verification as it could be a catch-all.

This function creates two columns returning a boolean value for emailvalid and catchall return values.



async function emailvalue(row) {
  console.log("Inside Function");
  console.log("Google Spreadsheet Title: " + doc.title);
  // Read email value
  const sheet = doc.sheetsByIndex[0];
  const rows = await sheet.getRows();
  const emailtocheck = rows[row].Email;
  const catchallemailtocheck = "random" + emailtocheck;

  // Run email verification for deliverable
  var result = await verifyemail(emailtocheck);
  console.log("Result", result);
  deliverable = result.success;
  console.log("deliverable =>", deliverable);

  // Run catchall search
  var catchallresult = await verifyemail(catchallemailtocheck);
  console.log("Catch all Result", catchallresult);
  catchalldeliverable = catchallresult.success;
  console.log("deliverable =>", catchalldeliverable);

  // Write email value
  rows[row].validemail = deliverable;
  rows[row].catchall = catchalldeliverable;
  await rows[row].save(); // save changes
}

Finally, we loop through our email verification function to process each email row-by-row:


getCsvData().then(async () => {
  for (let emailcount = row; emailcount < endrow; emailcount++) { await new Promise((resolve, reject) => setTimeout(resolve, 1000));
    console.log("Inside loop");
    emailvalue(emailcount);
  }
});

Here is how the final file looks:


// This script uses pingServer MX handshake.
// 1. It takes email address from Google spreadsheet
// 2. It checks if email is valid using MX handshake
// 3. It adds 'random' to check if it's a catchall account
// 4. It returns true | false for points 2 and 3 in Google spreadsheets

const verifyemail = require("./pingServer");
const { GoogleSpreadsheet } = require("google-spreadsheet");

let deliverable;
const row = 0;
const endrow = 110;

const credentials = require("./automation-machine.json");
const doc = new GoogleSpreadsheet(
  ""
);

async function getCsvData() {
  await doc.useServiceAccountAuth(credentials);
  await doc.loadInfo();
}

async function emailvalue(row) {
  console.log("Inside Function");
  console.log("Google Spreadsheet Title: " + doc.title);
  // Read email value
  const sheet = doc.sheetsByIndex[0];
  const rows = await sheet.getRows();
  const emailtocheck = rows[row].Email;
  const catchallemailtocheck = "random" + emailtocheck;

  console.log("Total rows to check: " + rows.length);
  console.log(" ");

  console.log(" ====== ROW IN CHECK ========");
  console.log(rows[row].Email);
  console.log(rows[row].validemail);
  console.log(rows[row].catchall);
  console.log(rows[row].outlookprotection);
  console.log(" ======  END  ========");
  console.log(" ");

  // Run email verification for deliverable
  var result = await verifyemail(emailtocheck);
  console.log("Result", result);
  deliverable = result.success;
  console.log("deliverable =>", deliverable);

  // Run catchall search
  var catchallresult = await verifyemail(catchallemailtocheck);
  console.log("Catch all Result", catchallresult);
  catchalldeliverable = catchallresult.success;
  console.log("deliverable =>", catchalldeliverable);

  // Write email value
  rows[row].validemail = deliverable;
  rows[row].catchall = catchalldeliverable;
  await rows[row].save(); // save changes
}

getCsvData().then(async () => {
  for (let emailcount = row; emailcount < endrow; emailcount++) { await new Promise((resolve, reject) => setTimeout(resolve, 1000));
    console.log("Inside loop");
    emailvalue(emailcount);
  }
});

Next step: Start Verifying Emails

📌 Step 5: Start Verifying Emails

To start verifying emails, paste your emails into the first column of your specified Google Sheet and Tab.

Remember to specify the From and To rows that you want to verify. I suggest starting small and start with around 50 rows. Change these variables on emailVerifier.js.

const row = 0 // start row to verify
const endrow = 50 // end row to verify

From the terminal, run the following command:

node emailVerifier.js

You’ll see the email verification happening asynchronously. I love watching it each time! It will look something like this:

Next step: What is next?

💡 What’s next?

There are some other areas to think about when doing email marketing and outreach. Here’s what I am thinking about:

1. How to gather real-time data from public sources (companies)
2. How to gather real-time data from public sources (people associated with companies)
3. How to build more automation into gathering data and email outreach

I’m currently working full-time on Drag with Duda and will continue sharing the lessons that I am learning on Email Outreach.

We are on a mission to help other Founders and so I hope that it proves useful. Good luck and reach out with any questions.

More free tutorials to follow shortly when I have the spare time to code (and write the notes up).

Sorry for the simple code also, I’m slowly learning 🙂

Try Google Sheets Add-on