Sending personalised emails to a list of recipients is one of the most common Apps Script use cases. By reading from a Google Sheet and building dynamic email content per row, you can send hundreds of unique emails in a single script run — while tracking which ones have already been sent.
Basic Bulk Email Loop
functionsendBulkEmails(){var sheet =SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sales Tracker");var data = sheet.getRange(2,1, sheet.getLastRow()-1,7).getValues();// Columns: [Customer Name, Email, Product, Amount, Region, Sales Rep, Status] data.forEach(function(row){var customerName = row[0];var email = row[1];var product = row[2];var amount = row[3];var subject ="Welcome to Acme Corp – Your "+ product +" is Active";var body ="Hi "+ customerName +",\n\nYour "+ product +" ($"+ amount +"/mo) is now active.\n\nBest,\nAcme Corp Team";GmailApp.sendEmail(email, subject, body);Logger.log("Sent to: "+ email);});}
Tracking Sent Emails with a Status Column
To avoid sending duplicates on re-runs, add an Email Sent column (column I) and check it before sending:
functionsendBulkEmailsWithTracking(){var sheet =SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sales Tracker");var lastRow = sheet.getLastRow();var data = sheet.getRange(2,1, lastRow -1,9).getValues();// Columns: [Customer Name, Email, Product, Amount, Region, Sales Rep, Status, Date, Email Sent] data.forEach(function(row, index){var customerName = row[0];var email = row[1];var product = row[2];var amount = row[3];var salesRep = row[5];var status = row[6];var emailSent = row[8];// Column Iif(status !=="Closed")return;if(emailSent ==="Sent")return;// Skip already-sent rowsvar firstName = customerName.split(" ")[0];var subject ="Welcome to Acme Corp, "+ firstName +"!";var htmlBody =` <p>Hi ${firstName},</p>
<p>Thank you for choosing the <strong>${product}</strong> plan at <strong>$${amount}/month</strong>.</p>
<p>Your dedicated account manager is <strong>${salesRep}</strong>. Don't hesitate to reach out with any questions.</p>
<br>
<p>Best regards,<br>The Acme Corp Team</p>
`;GmailApp.sendEmail(email, subject,"",{htmlBody: htmlBody });// Mark as sent in the sheet sheet.getRange(index +2,9).setValue("Sent");Logger.log("Email sent to "+ email);});}
With the sample data, emails go to Sarah Johnson and Lisa Park (Closed). Mark Chen is skipped. On re-runs, the Sent flag prevents duplicates.
Sending a Weekly Digest to Sales Reps
This groups closed deals by sales rep and sends each rep their own summary: