Article content and detailed guides remain in English. The selected language applies to controls and quick instructions.

Back to articles

Google Sheets for Email List Management: Formulas, Scripts and Workflows

On this page

Why Google Sheets for Email Lists

Google Sheets is free, collaborative and accessible from any device. For teams that do not need a full CRM or data warehouse, it serves as a practical email list management tool. Sheets handles lists of up to about 50,000 rows efficiently and can automate common tasks with built-in formulas and Apps Script:

Strength Limitation
Free and accessible Performance degrades above 50,000-100,000 rows
Real-time collaboration No built-in email verification
Familiar spreadsheet interface Manual import/export to email tools
Built-in formulas for data cleaning No automated suppression list management
Apps Script for automation Limited data types (no native date/time validation)
Easy sharing with permissions Version control is basic (revision history only)
Google Forms integration for collection No built-in consent tracking

Essential Formulas for Email Data

Cleaning and normalisation

Task Formula Example
Trim whitespace =TRIM(A2) " user@example.com " becomes "user@example.com"
Convert to lowercase =LOWER(A2) "User@Example.COM" becomes "user@example.com"
Trim and lowercase =LOWER(TRIM(A2)) Combines both operations
Remove non-breaking spaces =SUBSTITUTE(A2, CHAR(160), "") Removes hidden spaces from copy-paste
Full cleanup =LOWER(TRIM(SUBSTITUTE(A2, CHAR(160), ""))) Complete normalisation in one formula

Extraction and parsing

Task Formula Result
Extract domain =RIGHT(A2, LEN(A2) - FIND("@", A2)) "user@example.com" gives "example.com"
Extract local part =LEFT(A2, FIND("@", A2) - 1) "user@example.com" gives "user"
Extract TLD =RIGHT(A2, LEN(A2) - FIND(".", A2, FIND("@", A2))) "user@example.com" gives "com"
Extract first name from "First Last" =LEFT(B2, FIND(" ", B2) - 1) "John Smith" gives "John"
Extract last name from "First Last" =RIGHT(B2, LEN(B2) - FIND(" ", B2)) "John Smith" gives "Smith"
Extract email from "Name <email>" =MID(A2, FIND("<", A2) + 1, FIND(">", A2) - FIND("<", A2) - 1) "John john@example.com" gives "john@example.com"

Validation checks

Task Formula Returns
Contains @ =ISNUMBER(FIND("@", A2)) TRUE if @ is present
Contains dot after @ =ISNUMBER(FIND(".", A2, FIND("@", A2))) TRUE if domain has a dot
Basic email format check =AND(ISNUMBER(FIND("@", A2)), ISNUMBER(FIND(".", A2, FIND("@", A2))), LEN(A2) > 5, LEN(A2) < 255) TRUE if basic format is valid
Is role-based =OR(LEFT(A2, FIND("@", A2) - 1) = "info", LEFT(A2, FIND("@", A2) - 1) = "admin", LEFT(A2, FIND("@", A2) - 1) = "support", LEFT(A2, FIND("@", A2) - 1) = "sales") TRUE if local part matches common role-based prefixes
Is free email provider =OR(RIGHT(A2, LEN(A2) - FIND("@", A2)) = "gmail.com", RIGHT(A2, LEN(A2) - FIND("@", A2)) = "yahoo.com", RIGHT(A2, LEN(A2) - FIND("@", A2)) = "hotmail.com", RIGHT(A2, LEN(A2) - FIND("@", A2)) = "outlook.com") TRUE if domain is a major free provider

Deduplication

Task Formula How it works
Flag duplicates =COUNTIF(A$2:A2, A2) > 1 TRUE for the second and subsequent occurrences
First occurrence only =COUNTIF(A$2:A2, A2) = 1 TRUE only for the first occurrence
Count occurrences =COUNTIF(A:A, A2) Number of times this email appears
Remove duplicates (built-in) Data > Data cleanup > Remove duplicates Built-in feature; works on selected columns
UNIQUE function =UNIQUE(A2:A) Returns deduplicated list in a new column

Domain analysis

Task Formula Use
Count emails per domain =COUNTIF(DomainColumn, DomainCell) Identify concentrations
Most common domains =QUERY(A:B, "SELECT B, COUNT(A) GROUP BY B ORDER BY COUNT(A) DESC LIMIT 20") Top 20 domains (requires domain in column B)
Percentage by domain =COUNTIF(B:B, B2) / COUNTA(B2:B) Share of list from each domain

Spreadsheet Structure

Recommended column layout

Column Header Purpose Example
A email_raw Original email as imported " John@Example.COM "
B email_clean Normalised email "john@example.com"
C domain Extracted domain "example.com"
D first_name First name "John"
E last_name Last name "Smith"
F company Company name "Example Corp"
G title Job title "VP Sales"
H source Where this contact came from "Webinar - Oct 2026"
I date_added When the contact was added "2026-10-08"
J consent_type How consent was obtained "double opt-in"
K is_valid Basic format validation TRUE/FALSE
L is_duplicate Duplicate flag TRUE/FALSE
M is_role_based Role-based email flag TRUE/FALSE
N status Active, unsubscribed, bounced "active"
O notes Free-text notes "Met at conference"

Multiple sheets for organisation

Sheet name Purpose Contents
Master List Primary contact database All contacts with full data
New Imports Staging area for new contacts Raw imports before cleaning
Suppression Unsubscribed and bounced addresses Addresses that must not be emailed
Domain Analysis Domain-level metrics Domain counts, percentages
Campaign Segments Filtered views for campaigns Contacts filtered by criteria
Audit Log Change history Who changed what and when

Apps Script Automation

Auto-clean new entries

function cleanEmailColumn() {
  var sheet = SpreadsheetApp.getActiveSheet();
  var range = sheet.getRange('A2:A' + sheet.getLastRow());
  var values = range.getValues();
  var cleanColumn = sheet.getRange('B2:B' + sheet.getLastRow());
  var cleanValues = [];

  for (var i = 0; i < values.length; i++) {
    var email = values[i][0];
    if (email && typeof email === 'string') {
      // Trim, lowercase, remove non-breaking spaces
      var cleaned = email.trim().toLowerCase()
        .replace(/ /g, '');
      cleanValues.push([cleaned]);
    } else {
      cleanValues.push(['']);
    }
  }

  cleanColumn.setValues(cleanValues);
}

Flag duplicates automatically

function flagDuplicates() {
  var sheet = SpreadsheetApp.getActiveSheet();
  var emailRange = sheet.getRange(
    'B2:B' + sheet.getLastRow()
  );
  var emails = emailRange.getValues();
  var dupColumn = sheet.getRange(
    'L2:L' + sheet.getLastRow()
  );
  var seen = {};
  var flags = [];

  for (var i = 0; i < emails.length; i++) {
    var email = emails[i][0];
    if (!email) {
      flags.push([false]);
      continue;
    }
    if (seen[email]) {
      flags.push([true]);
    } else {
      seen[email] = true;
      flags.push([false]);
    }
  }

  dupColumn.setValues(flags);
}

Check against suppression list

function checkSuppression() {
  var masterSheet = SpreadsheetApp.getActiveSpreadsheet()
    .getSheetByName('Master List');
  var suppressionSheet = SpreadsheetApp.getActiveSpreadsheet()
    .getSheetByName('Suppression');

  // Load suppression list into a set
  var suppRange = suppressionSheet.getRange(
    'A2:A' + suppressionSheet.getLastRow()
  );
  var suppEmails = suppRange.getValues();
  var suppSet = {};
  for (var i = 0; i < suppEmails.length; i++) {
    if (suppEmails[i][0]) {
      suppSet[suppEmails[i][0].toLowerCase().trim()] = true;
    }
  }

  // Check master list against suppression
  var emailRange = masterSheet.getRange(
    'B2:B' + masterSheet.getLastRow()
  );
  var emails = emailRange.getValues();
  var statusColumn = masterSheet.getRange(
    'N2:N' + masterSheet.getLastRow()
  );
  var statuses = statusColumn.getValues();

  for (var j = 0; j < emails.length; j++) {
    var email = emails[j][0];
    if (email && suppSet[email.toLowerCase().trim()]) {
      statuses[j][0] = 'suppressed';
    }
  }

  statusColumn.setValues(statuses);
}

Scheduled cleanup trigger

function createDailyTrigger() {
  ScriptApp.newTrigger('dailyCleanup')
    .timeBased()
    .everyDays(1)
    .atHour(6)
    .create();
}

function dailyCleanup() {
  cleanEmailColumn();
  flagDuplicates();
  checkSuppression();

  // Log the run
  var logSheet = SpreadsheetApp.getActiveSpreadsheet()
    .getSheetByName('Audit Log');
  logSheet.appendRow([
    new Date(),
    'Daily cleanup',
    'Completed',
    Session.getActiveUser().getEmail()
  ]);
}

Google Forms Integration

Collecting emails via Google Forms

Setup step Details
Create a Google Form Include email field (use built-in email validation)
Link to Google Sheet Form responses automatically populate a sheet
Add consent checkbox Required checkbox with consent language
Set up notifications Email notification when new responses arrive
Add cleanup trigger Apps Script trigger to clean new entries automatically

Form response processing

function onFormSubmit(e) {
  var sheet = SpreadsheetApp.getActiveSheet();
  var row = sheet.getLastRow();

  // Get the email from the response
  var email = sheet.getRange(row, 2).getValue(); // Column B
  if (email) {
    // Clean and write to clean column
    var cleaned = email.trim().toLowerCase();
    sheet.getRange(row, 3).setValue(cleaned); // Column C

    // Extract domain
    var domain = cleaned.split('@')[1] || '';
    sheet.getRange(row, 4).setValue(domain); // Column D

    // Set date added
    sheet.getRange(row, 5).setValue(new Date()); // Column E

    // Set source
    sheet.getRange(row, 6).setValue('Google Form'); // Column F
  }
}

Integrating with Email Tools

Export formats

Email tool Preferred import format Key fields required
Mailchimp CSV Email, first name, last name, tags
HubSpot CSV Email, first name, last name, company
Salesforce CSV Email, first name, last name, company, title
ActiveCampaign CSV Email, first name, last name, tags
Brevo CSV Email, first name, last name
Klaviyo CSV Email, first name, last name, properties

Export workflow

Step Action
1 Filter master list for the segment you want to export
2 Remove suppressed, bounced, and unsubscribed contacts
3 Select only the columns needed by the destination tool
4 Download as CSV (File > Download > Comma Separated Values)
5 Import CSV into destination tool
6 Log the export in the Audit Log sheet

Collaborative Workflows

Sharing and permissions

Permission level Who gets it What they can do
Owner Sheet creator / admin Full control; manage sharing
Editor Team members who update data Add, edit, delete rows; run scripts
Commenter Reviewers who suggest changes Add comments; cannot edit directly
Viewer Stakeholders who need visibility View only; can download

Data validation rules

Column Validation rule Settings
Email Text contains "@" Show warning on invalid entry
Source Dropdown list of approved sources Reject input not in list
Consent type Dropdown (double opt-in, single opt-in, implied, purchased) Reject input not in list
Status Dropdown (active, unsubscribed, bounced, suppressed) Reject input not in list
Date added Date Reject non-date input

Importing Data from Email Extractor

After extracting emails from files using Email Extractor, download the results as CSV. Import the CSV into the "New Imports" sheet in your Google Sheets workbook (File > Import > Upload), then run the cleanup scripts to normalise, deduplicate and check against your suppression list before merging into the Master List.

Extract emails

Explore tools

Verify emails

Check address validity before using your list.

ZeroBounce

Email Verification

Verifies email lists and provides tools for monitoring deliverability.

Useful when list cleaning and sender health belong in one workflow.

Explore ZeroBounce (opens in a new tab)