How to Verify Emails in Google Sheets with Apps Script

You've got 10,000 email addresses sitting in a Google Sheet. Maybe they're leads from a webinar, contacts exported from your CRM, or a purchased list your sales team wants to blast. Here's the problem: somewhere between 20% and 30% of those addresses are probably invalid. Sending to them will wreck your sender reputation, spike your bounce rate, and could even get your domain blacklisted.

Google Sheets has a built-in ISEMAIL function, but it only checks if an email looks right - it can't tell you whether the mailbox actually exists on the other end. That's like checking if a phone number has the right number of digits without ever dialing it.

In this guide, you'll build a custom Google Apps Script that connects to the Bulk Email Checker API to verify every email address in your spreadsheet. No add-ons to install, no data leaving your sheet except for the API calls. You'll have a working verification workflow in about 15 minutes.

Why ISEMAIL Isn't Enough for Real Email Verification

Google Sheets email validation checks formatting only. It confirms that something@domain.com follows the right pattern. But formatting validation misses the things that actually cause bounces: non-existent mailboxes, expired domains, disposable email services, and spam traps.

Validation Method Syntax Check Domain Check Mailbox Exists Spam Trap Detection
ISEMAIL Function Yes No No No
Regex Formula Yes No No No
Apps Script + API Yes Yes Yes Yes

Real email verification needs to ping the actual mail server. It checks DNS records, confirms the MX server responds, and performs an SMTP handshake to see if the mailbox is live. That's what the Bulk Email Checker API does - and with Google Apps Script, you can trigger that entire process from inside your spreadsheet.

📊
Key Stat: Email lists decay at roughly 22% per year. If you haven't verified your Google Sheets contact list in 6 months, over 10% of addresses could be invalid right now.

What You'll Need Before Starting

This is a straightforward setup. You don't need to be a JavaScript expert - if you can copy and paste code, you can do this.

Here's what you need:

  • A Google account with access to Google Sheets
  • A Bulk Email Checker account (you get 10 free verifications daily to test)
  • Your API key from the Bulk Email Checker dashboard
  • A spreadsheet with email addresses in a column

The API uses a simple REST endpoint - you send it an email address and your API key, and it sends back a JSON response telling you everything about that address. No complex authentication, no OAuth flows. Just a URL with parameters.

Setting Up Your Google Sheet

Create a new Google Sheet (or open an existing one with your email list). Structure it like this:

Column A Column B Column C Column D Column E
Email Status Details Disposable Free Service
john@example.com (filled by script) (filled by script) (filled by script) (filled by script)

Column A holds your email addresses. Columns B through E will be populated automatically by the Apps Script. You can add more columns later for things like role-based detection or MX enrichment data - the API returns over 15 data points per email.

💡
Pro Tip: Add a header row first. The script will skip row 1 automatically, so your headers won't get processed as email addresses.

Writing the Apps Script

Open the Script Editor by going to Extensions > Apps Script in your Google Sheet. Delete any placeholder code in the editor. You'll paste in two functions: one for verifying a single email, and one for processing your entire list in batches.

Single Email Verification Function

This function handles the API call to Bulk Email Checker and returns the result. It's the core building block.

JavaScript (Apps Script)
// Replace with your actual API key from BulkEmailChecker.com
var API_KEY = 'YOUR_API_KEY';

function verifyEmail(email) {
  // Skip empty cells
  if (!email || email.toString().trim() === '') {
    return null;
  }
  
  // Clean the email
  email = email.toString().trim().toLowerCase();
  
  // Build the API URL
  var url = 'https://api.bulkemailchecker.com/real-time/'
    + '?key=' + API_KEY
    + '&email=' + encodeURIComponent(email);
  
  try {
    // Make the API request
    var response = UrlFetchApp.fetch(url, {
      muteHttpExceptions: true
    });
    
    var result = JSON.parse(response.getContentText());
    
    return {
      status: result.status || 'unknown',
      event: result.event || 'no_response',
      details: result.details || '',
      isDisposable: result.isDisposable || false,
      isFreeService: result.isFreeService || false,
      isRoleAccount: result.isRoleAccount || false,
      emailSuggested: result.emailSuggested || ''
    };
    
  } catch (error) {
    // Handle network errors gracefully
    return {
      status: 'error',
      event: 'api_error',
      details: error.toString(),
      isDisposable: false,
      isFreeService: false,
      isRoleAccount: false,
      emailSuggested: ''
    };
  }
}

That function does a few important things. It cleans up the email address (trimming whitespace, converting to lowercase), calls the Bulk Email Checker real-time API, and returns a structured object with the verification results. The muteHttpExceptions: true flag prevents the script from crashing on HTTP errors.

Batch Verification for Large Lists

Now for the function that processes your entire spreadsheet. This one reads Column A, verifies each email, and writes the results to Columns B through E.

JavaScript (Apps Script)
function verifyAllEmails() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  var lastRow = sheet.getLastRow();
  
  // Nothing to process
  if (lastRow < 2) {
    SpreadsheetApp.getUi().alert('No emails found. Add emails starting from row 2.');
    return;
  }
  
  // Grab all emails from Column A (skip header row)
  var emailRange = sheet.getRange(2, 1, lastRow - 1, 1);
  var emails = emailRange.getValues();
  
  // Set up the headers if they're missing
  sheet.getRange(1, 1).setValue('Email');
  sheet.getRange(1, 2).setValue('Status');
  sheet.getRange(1, 3).setValue('Details');
  sheet.getRange(1, 4).setValue('Disposable');
  sheet.getRange(1, 5).setValue('Free Service');
  sheet.getRange(1, 6).setValue('Role Account');
  sheet.getRange(1, 7).setValue('Suggested Fix');
  
  var processed = 0;
  var total = emails.length;
  
  for (var i = 0; i < emails.length; i++) {
    var email = emails[i][0];
    var currentRow = i + 2; // offset for header
    
    // Skip if already verified (Column B has data)
    var existingStatus = sheet.getRange(currentRow, 2).getValue();
    if (existingStatus !== '') {
      continue;
    }
    
    // Skip empty cells
    if (!email || email.toString().trim() === '') {
      continue;
    }
    
    // Verify the email
    var result = verifyEmail(email);
    
    if (result) {
      // Write results to the row
      sheet.getRange(currentRow, 2).setValue(result.status);
      sheet.getRange(currentRow, 3).setValue(result.event);
      sheet.getRange(currentRow, 4).setValue(result.isDisposable ? 'Yes' : 'No');
      sheet.getRange(currentRow, 5).setValue(result.isFreeService ? 'Yes' : 'No');
      sheet.getRange(currentRow, 6).setValue(result.isRoleAccount ? 'Yes' : 'No');
      sheet.getRange(currentRow, 7).setValue(result.emailSuggested);
      
      // Color-code the status cell
      if (result.status === 'passed') {
        sheet.getRange(currentRow, 2).setBackground('#dcfce7');
      } else if (result.status === 'failed') {
        sheet.getRange(currentRow, 2).setBackground('#fee2e2');
      } else {
        sheet.getRange(currentRow, 2).setBackground('#fef3c7');
      }
    }
    
    processed++;
    
    // Pause briefly to respect rate limits
    // Adjust this based on your API plan
    Utilities.sleep(500);
    
    // Google Apps Script has a 6-minute execution limit
    // Save progress every 50 emails
    if (processed % 50 === 0) {
      SpreadsheetApp.flush();
    }
  }
  
  SpreadsheetApp.getUi().alert(
    'Verification complete! Processed ' + processed + ' of ' + total + ' emails.'
  );
}

A few things worth noting about this batch function. It skips rows that already have a status (so you can run it again without re-verifying everything). It adds a 500ms pause between API calls to stay within rate limits. And it flushes results to the sheet every 50 emails - so if the script times out, you don't lose your progress.

⚠️
Warning: Google Apps Script has a 6-minute execution limit per run. For lists over 500 emails, you'll want to run the script multiple times. It automatically picks up where it left off since it skips already-verified rows.

Running Your First Verification

To run the verification, follow these steps:

  1. Save the script - Click the floppy disk icon or press Ctrl+S in the Apps Script editor
  2. Select the function - In the dropdown next to the Run button, choose verifyAllEmails
  3. Click Run - The first time, Google will ask you to authorize the script
  4. Grant permissions - You'll need to allow the script to access your spreadsheet and make external web requests
  5. Watch it work - Switch back to your spreadsheet and you'll see results populating in real time

The authorization step happens only once. Google flags the script as "unverified" since you wrote it yourself - just click "Advanced" and then "Go to [your project name]" to proceed. This is standard for custom Apps Scripts.

You can also add a custom menu to your spreadsheet so you don't have to open the script editor every time. Add this function to your script:

JavaScript (Apps Script)
function onOpen() {
  var ui = SpreadsheetApp.getUi();
  ui.createMenu('Email Verification')
    .addItem('Verify All Emails', 'verifyAllEmails')
    .addItem('Verify Selected Row', 'verifySelectedRow')
    .addToUi();
}

function verifySelectedRow() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  var row = sheet.getActiveCell().getRow();
  
  if (row < 2) {
    SpreadsheetApp.getUi().alert('Select a row with an email address (row 2 or below).');
    return;
  }
  
  var email = sheet.getRange(row, 1).getValue();
  var result = verifyEmail(email);
  
  if (result) {
    sheet.getRange(row, 2).setValue(result.status);
    sheet.getRange(row, 3).setValue(result.event);
    sheet.getRange(row, 4).setValue(result.isDisposable ? 'Yes' : 'No');
    sheet.getRange(row, 5).setValue(result.isFreeService ? 'Yes' : 'No');
    sheet.getRange(row, 6).setValue(result.isRoleAccount ? 'Yes' : 'No');
    sheet.getRange(row, 7).setValue(result.emailSuggested);
    
    if (result.status === 'passed') {
      sheet.getRange(row, 2).setBackground('#dcfce7');
    } else if (result.status === 'failed') {
      sheet.getRange(row, 2).setBackground('#fee2e2');
    } else {
      sheet.getRange(row, 2).setBackground('#fef3c7');
    }
  }
}

After saving, reload your spreadsheet and you'll see a new "Email Verification" menu in the toolbar. Click it to verify all emails or just the row you've selected.

Understanding and Handling Results

The Bulk Email Checker API returns three primary statuses. Here's what each one means and what you should do with it:

What does each status mean?

Email verification produces three possible outcomes. A passed status means the mailbox exists and can receive email - these are safe to send to. A failed status means the email is invalid for some reason (mailbox doesn't exist, domain is dead, syntax error). And unknown means the server couldn't give a definitive answer, usually because it's a catch-all domain or the server was temporarily unavailable.

Status What It Means Action to Take
passed Mailbox verified as valid and deliverable Safe to send. Keep in your list.
failed Email is invalid or undeliverable Remove immediately. Sending will bounce.
unknown Can't confirm - catch-all domain or greylisting Use caution. Send in small batches.

Beyond the status, pay attention to the boolean flags. If isDisposable comes back as "Yes," that address is from a temporary email service like Mailinator or Guerrilla Mail. These people didn't give you a real email. If isRoleAccount is flagged, the address is something like info@company.com or admin@company.com - these often have multiple recipients and higher complaint rates.

💡
Pro Tip: Check the emailSuggested column. If someone typed "john@gmial.com," the API will suggest "john@gmail.com." You can use this to recover valid contacts from typos instead of just deleting them.

Automating the Workflow with Time-Based Triggers

If new emails are constantly flowing into your sheet (from form submissions, CRM exports, or data imports), you can set up an automatic trigger to verify them on a schedule.

In the Apps Script editor, click the clock icon on the left sidebar (Triggers). Then click "Add Trigger" and configure it like this:

  • Function to run: verifyAllEmails
  • Event source: Time-driven
  • Type of time-based trigger: Hour timer
  • Interval: Every 4 hours (or whatever fits your workflow)

Since the script skips already-verified rows, the trigger will only process new additions. This creates a hands-off email verification workflow that runs continuously without any manual intervention.

For high-volume use cases where you're processing thousands of emails daily, Bulk Email Checker offers unlimited API plans with thread-based scaling that won't throttle your verification speed.

Action Required: Replace 'YOUR_API_KEY' in the script with your actual API key. Grab one free from BulkEmailChecker.com - you get 10 free verifications per day to test the integration.

Troubleshooting Common Issues

How do I fix "Exception: Request failed" errors?

This usually means the API key is wrong or your account has run out of credits. Double-check your key in the script. Log into your Bulk Email Checker dashboard to confirm your credit balance. The pay-as-you-go pricing starts at $0.001 per verification and credits never expire, so you can top up and resume whenever you need to.

The script times out before finishing my list

Google Apps Script enforces a 6-minute execution limit. For large lists, just run the script multiple times - it picks up where it left off. If you're verifying 10,000+ emails regularly, consider writing a more advanced version that uses continuation tokens or processes emails in scheduled batches of 300-400 at a time.

Some emails show "unknown" status

An unknown result means the mail server didn't give a clear yes or no. This happens with catch-all domains (servers that accept all incoming mail regardless of the mailbox) and servers that use greylisting. Don't automatically delete these - they might be valid. Instead, segment them separately and send to them cautiously with smaller batch sizes.

Can I use this with Google Forms?

Absolutely. If your Google Form responses feed into a Sheet, the time-based trigger will pick up new submissions automatically. You could also modify the script to use an onFormSubmit trigger for instant verification the moment someone fills out your form.

Frequently Asked Questions

How accurate is API-based email verification from Google Sheets?

The accuracy depends entirely on the verification service you're using. Bulk Email Checker achieves 99.7% accuracy by performing a 17+ factor verification process that includes syntax checks, DNS validation, MX record lookups, SMTP handshake verification, and mailbox confirmation. The Google Sheets integration doesn't reduce this accuracy - the API does all the heavy lifting server-side.

How many emails can I verify per day with this method?

Google Apps Script allows up to about 20,000 UrlFetchApp calls per day for regular Gmail accounts (and more for Google Workspace accounts). Combined with the 6-minute execution limit per run, you can realistically verify 500-700 emails per script execution. Running the script multiple times throughout the day gets you through larger lists without issues.

Is the data secure when using an external API from Google Sheets?

The API calls from Google Apps Script use HTTPS encryption. Bulk Email Checker is ISO 27001 certified, SOC 2 Type II compliant, and follows GDPR requirements. Email addresses are processed in real-time and aren't stored on their servers after verification. Your spreadsheet data stays in your Google account - only the individual email address being verified is sent to the API endpoint during each call.

Does this work with Google Workspace (formerly G Suite) accounts?

Yes. Google Workspace accounts actually have higher quotas for Apps Script - up to 100,000 URL fetch calls per day versus 20,000 for free Gmail accounts. This means you can verify significantly more emails per day. The setup process is identical.

Can I verify emails in real-time as they're entered into the sheet?

You can set up an onEdit trigger that fires whenever someone types in a cell. However, this approach has some quirks with Apps Script's execution model. A more reliable method is the onFormSubmit trigger for form-linked sheets, or a short-interval time-based trigger (every 5 minutes) that catches new entries quickly.

Building email verification into your Google Sheets workflow doesn't require expensive add-ons or complicated integrations. With a few lines of Apps Script and the Bulk Email Checker API, you can validate entire contact lists directly inside the tool you're already using. Start with the free daily verifications to test your setup, then scale up to pay-as-you-go credits when you're ready to clean your full list.

99.7% Accuracy Guarantee

Stop Bouncing. Start Converting.

Millions of emails verified daily. Industry-leading SMTP validation engine.