Tool Handshake

How to Send Emails From Google Sheets Automatically Without Paid Add-ons

Send emails from Google Sheets automatically for free with Apps Script. Copy-paste code, a daily trigger, and the 100 per day limit for Gmail accounts.

By Archit Tamrakar · · 7 min read

Quick answer: Open your sheet, go to Extensions > Apps Script, paste the script below, and add a daily trigger. Free? Yes. Time: about 15 minutes.

You can send emails from Google Sheets automatically with Apps Script, Google's built-in scripting tool, and no paid add-on. The main limit is the daily cap: Google's quota table (last updated 3 September 2026) allows 100 email recipients per day on consumer accounts and 1,500 on Google Workspace accounts.

What do you need before you start?

You need a Google account and a Google Sheet with a list of recipients. That is all. No credit card, no extra subscription.

Your sheet needs these column headings in row 1, spelled exactly like this, on a tab named Recipients:

NameEmailSubjectMessageStatus
Priyapriya@example.comYour invoiceHi {{Name}}, your invoice is attached to this thread.(leave blank)

Leave the Status column empty. The script fills it in. You can write {{Name}} in a message and the script swaps in the value from the Name column.

If you use a work or school account, your admin may restrict scripts, so ask them if the authorization step fails.

How do you send emails from Google Sheets automatically? (Method 1: Apps Script, free)

This method uses MailApp, which Google describes as a service whose sole purpose is sending email. It cannot read your inbox, so the permission it asks for is narrow: the script.send_mail scope.

  1. Open your Google Sheet.
  2. Click Extensions > Apps Script. This opens a script project tied to your sheet, as Google's menu guide describes.
  3. Delete the sample code in the editor.
  4. Paste the full script below.
  5. Save the project. Verify this step: the save icon or menu may differ in the current editor.
  6. Return to your sheet and refresh the page. A new menu called Auto Email appears.
  7. Click Auto Email > Send pending emails now.
  8. When Google asks you to authorize the script, follow the prompts. If you see This app isn't verified, click Advanced, then Go to {Project Name} (unsafe). Google's mail merge sample describes this same warning. It appears because you wrote the script yourself and Google has not reviewed it.
  9. Click Auto Email > Send pending emails now again. Each sent row now shows a time in Status.
// Sends one email per row of the "Recipients" tab.
// Columns (row 1): Name | Email | Subject | Message | Status
// Rows with a blank Status are sent; others are skipped.

const SHEET_NAME = 'Recipients';
const REQUIRED_HEADERS = ['Name', 'Email', 'Subject', 'Message', 'Status'];

function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu('Auto Email')
    .addItem('Send pending emails now', 'sendPendingEmails')
    .addToUi();
}

function sendPendingEmails() {
  const sheet = SpreadsheetApp.getActive().getSheetByName(SHEET_NAME);
  if (!sheet) {
    throw new Error('No tab named "' + SHEET_NAME + '" was found.');
  }

  const values = sheet.getDataRange().getValues();
  const headers = values.shift();
  const idx = {};
  REQUIRED_HEADERS.forEach(function (h) {
    idx[h] = headers.indexOf(h);
    if (idx[h] === -1) {
      throw new Error('Missing column heading: ' + h);
    }
  });

  let remaining = MailApp.getRemainingDailyQuota();
  const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

  for (let i = 0; i < values.length; i++) {
    const row = values[i];
    if (row[idx.Status] !== '') continue; // already handled

    const statusCell = sheet.getRange(i + 2, idx.Status + 1);
    const to = String(row[idx.Email]).trim();

    if (!emailPattern.test(to)) {
      statusCell.setValue('Skipped: invalid address');
      continue;
    }
    if (remaining < 1) {
      break; // out of quota; leave the rest blank for the next run
    }

    const body = String(row[idx.Message]).split('{{Name}}').join(row[idx.Name]);
    try {
      MailApp.sendEmail(to, String(row[idx.Subject]), body);
      remaining--;
      statusCell.setValue('Sent ' + new Date().toISOString());
    } catch (err) {
      statusCell.setValue('Error: ' + err.message);
    }
    SpreadsheetApp.flush(); // save progress after every send
  }
}

The script writes the result after every email. If it stops partway (for example at the six-minute run limit), the rows already sent are marked and will not be sent twice.

How do you make Google Sheets send the emails on a schedule? (Method 2: a time-driven trigger)

A trigger tells Google to run your function at set times, even when your sheet is closed. Google's trigger documentation says installable triggers always run under the account of the person who created them, and time-driven triggers can run as often as every minute or as rarely as once a month.

Option A: click it together in the editor.

  1. In the Apps Script editor, click Triggers (the alarm clock icon) on the left.
  2. Click Add Trigger at the bottom right.
  3. Choose sendPendingEmails as the function to run.
  4. Choose a time-driven event source and set how often it runs. Verify this step: the dropdown names in the dialog may differ.
  5. Click Save, and approve any permission prompt.

Option B: create it with code. Paste this function below the first script and run it once. It removes old copies of the trigger first, so you do not end up with duplicates.

function createDailyTrigger() {
  ScriptApp.getProjectTriggers().forEach(function (t) {
    if (t.getHandlerFunction() === 'sendPendingEmails') {
      ScriptApp.deleteTrigger(t);
    }
  });

  ScriptApp.newTrigger('sendPendingEmails')
    .timeBased()
    .atHour(8)
    .everyDays(1)
    .inTimezone('America/New_York')
    .create();
}

The ClockTriggerBuilder reference shows the same pattern: atHour() needs a frequency like everyDays(1), and the time zone defaults to the script's own if you leave out inTimezone(). Change 'America/New_York' to your own zone.

Now the workflow is: add rows with a blank Status, and the next daily run sends them.

Which method should you choose?

For a simple list, Method 1 plus Method 2 together is enough. Paid tools only matter if you need open tracking or non-technical teammates editing templates.

MethodCostDifficultyLimitsBest for
Menu button (Method 1)FreeEasySame Google quotas; you click itOne-off batches
Time-driven trigger (Method 2)FreeMediumSame Google quotas; 20 triggers per user per scriptDaily or weekly sends
Paid mail-merge add-onVaries by vendorEasyVendor plan limits plus Google's ownOpen tracking, no coding

What's free and what costs money?

Everything above is free. The only limits are Google's. As of 12 October 2026, the Apps Script quota page lists:

LimitConsumer (gmail.com)Google Workspace
Email recipients per day1001,500
Email recipients per day, within your domain1002,000
Email recipients per message5050
Attachments per message250250
Script runtime per execution6 minutes6 minutes
Triggers total runtime per day90 minutes6 hours

Google says quotas are per user and reset 24 hours after the first request. It also says all quotas can be eliminated, reduced or changed at any time without notice. To check your balance at any time, call MailApp.getRemainingDailyQuota(), which returns the number of recipients you can still email that day.

What can go wrong, and how do you fix it?

  1. "Missing column heading" error. The script looks for exact headings. Check spelling and capital letters in row 1, and confirm the tab is named Recipients.
  2. Nothing sends and Status stays blank. Either every row already has a Status value, or the daily quota is used up. Clear the cell to retry a row, or wait for the quota to reset.
  3. "This app isn't verified" warning. Expected for your own scripts. Click Advanced, then Go to {Project Name} (unsafe), as Google's sample describes. Only do this for scripts you wrote or fully read.
  4. The trigger stopped working. Open Executions in the left panel of the editor to see errors. When a trigger fails, Google also sends a summary email titled "Summary of failures for Apps Script", according to its trigger documentation.
  5. The run stops partway through a long list. A single execution is capped at 6 minutes. Because the script saves progress after each send, the next run continues where it left off.
  6. A row says "Error:" and a message. Read the text in the Status cell. Common causes are a bad address or the quota limit. Fix the cell, clear Status, and run again.

Is this allowed?

Sending from your own account with Apps Script is allowed, but the content is still covered by email law. The US CAN-SPAM Act applies to all commercial email, including business-to-business mail. The FTC says marketing messages need:

  • accurate "From", "To" and "Reply-To" information,
  • a subject line that matches the content,
  • a valid physical postal address,
  • a clear way to opt out, honored within 10 business days.

The FTC states penalties of up to $53,088 for each violating email (guide edited January 2024). Add an unsubscribe line and your address to the Message column for any promotional send, and delete anyone who opts out.

Google has its own rules too. Its sender guidelines say senders of more than 5,000 messages per day to Gmail accounts must meet extra requirements, including SPF, DKIM and DMARC authentication and one-click unsubscribe for marketing mail. All senders are asked to keep the Postmaster Tools spam rate below 0.3%. A free account capped at 100 recipients a day will not reach that volume, but the spam-rate guidance applies to everyone.

Do not use this method to send unsolicited bulk mail, to mislead recipients about who you are, or to get around another service's rules.

What's still unclear?

  • Quota changes. Google reserves the right to change limits without notice, so check the quota page before relying on a number.
  • Editor wording. Google's documentation names Triggers and Add Trigger but does not list every field in the trigger dialog, so the dropdown labels may differ from what you see.
  • Threshold wording. Google's sender guidelines use both "5,000 or more" and "more than 5,000" for the bulk-sender line. It makes no practical difference at free-account volumes.
  • Weakest point. For 100 recipients a day, this approach is cheap but basic. There is no open or click tracking, and any serious list growth will push you into Workspace limits or a dedicated sending service.

Last checked: 12 October 2026.

Frequently asked questions

Can Google Sheets send emails by itself without an add-on?

Yes. Google Sheets has no built-in send-email button, but its bound Apps Script editor does. You paste a short script that calls MailApp.sendEmail, then run it from a custom menu or a time-driven trigger. It costs nothing beyond the Google account you already have, and the script runs on Google's servers.

How many emails can I send per day from Google Sheets for free?

According to Google's quota table (last updated 3 September 2026), consumer accounts such as gmail.com can send to 100 email recipients per day. Google Workspace accounts can send to 1,500 recipients per day, or 2,000 within their own domain. Google says quotas can change at any time without notice.

Should I use MailApp or GmailApp?

Use MailApp if you only need to send. Google's reference says MailApp cannot access your Gmail inbox, and scripts that use GmailApp are more likely to trigger a re-authorization request. GmailApp is needed only when your script must read drafts or other Gmail data.

Will the emails send if my computer is off?

Yes, once you create a time-driven trigger. Apps Script runs on Google's servers, and installable triggers run under the account of the person who created them. If the trigger fails, Google sends a failure summary email from noreply-apps-scripts-notifications@google.com.

Is it legal to email a list from a Google Sheet?

It depends on the message. The US CAN-SPAM Act covers all commercial email, with no exception for business-to-business mail. Marketing messages need accurate headers, a physical postal address and a working opt-out honored within 10 business days. Purely personal or transactional messages face lighter rules.

What happens if my script hits the daily email limit?

The send call fails with an error and the script stops sending until the quota resets. Google says quotas reset 24 hours after the first request. The script below checks the remaining quota first and leaves unsent rows blank, so the next run picks them up.

Sources

  1. Class MailApp, Google for Developers (2026-04-13)
  2. Quotas for Google Services, Google for Developers (2026-09-03)
  3. Installable Triggers, Google for Developers (2026-10-12)
  4. Class ClockTriggerBuilder, Google for Developers (2026-10-12)
  5. Custom Menus in Google Workspace, Google for Developers (2026-09-03)
  6. Mail Merge with Gmail and Google Sheets, Google for Developers (2026-09-03)
  7. CAN-SPAM Act: A Compliance Guide for Business, Federal Trade Commission (2024-01-01)
  8. Email sender guidelines, Google Help (2026-10-12)