Tool Handshake

How to Connect Gmail to Google Sheets and Log Every Email Automatically (Free, With Apps Script)

Connect Gmail to Google Sheets for free with Apps Script. Copy the script, add a 5-minute trigger, and log every email in about 10 minutes.

By Archit Tamrakar · · 7 min read

Quick answer: Paste the script below into Extensions > Apps Script in a Google Sheet, run it once, then add a time-driven trigger so it checks Gmail every 5 minutes. Free? Yes, within Google's daily quotas. Time: about 10 minutes.

Google's own documentation confirms every piece this uses: GmailApp.search finds email, and a time-driven trigger can run the script on a schedule of 1, 5, 10, 15 or 30 minutes. Each email becomes one row with date, sender, recipient, subject and the start of the body.

What do you need before you start?

You need a Google account with Gmail, and a Google Sheet that you can edit. No paid plan is required. The script runs under your own account, so it can only see your own mailbox.

Two limits are worth knowing now. As of 12 October 2026, Google's quota page lists 90 minutes per day of total trigger runtime for consumer (gmail.com) accounts, 20 triggers per user per script, and 6 minutes per single execution. Google says quotas can change without notice.

How do you connect Gmail to Google Sheets and log every email? (Method 1: free script)

This method uses one script and no add-ons. The script finds inbox threads that do not yet have a label called logged-to-sheet, writes one row per message, then adds the label so the thread is not logged twice.

  1. Open a new Google Sheet. Name it something like "Email log".
  2. Click Extensions > Apps Script. This is the documented way to open the editor from a sheet.
  3. Delete the sample code in the editor.
  4. Paste the script below.
  5. Click the Save project icon (the disk).
  6. In the function drop-down at the top of the editor, choose logEmails, then click Run.
  7. When Google asks for permission, click through the prompts and approve access to your Gmail and Sheets. Verify this step: the wording and buttons on the permission screens may differ.
  8. Go back to your sheet. A tab named Emails now holds up to 100 logged threads.
// Logs Gmail messages to a sheet tab named "Emails".
// Bound to the spreadsheet via Extensions > Apps Script.

const SHEET_NAME = 'Emails';
const LABEL_NAME = 'logged-to-sheet';
// Same operators as the Gmail search box. Change to narrow what is logged.
const SEARCH_QUERY = 'in:inbox -label:' + LABEL_NAME;
const MAX_THREADS_PER_RUN = 100;
const BODY_PREVIEW_CHARS = 500;

function logEmails() {
  const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  let sheet = spreadsheet.getSheetByName(SHEET_NAME);
  if (!sheet) {
    sheet = spreadsheet.insertSheet(SHEET_NAME);
  }
  if (sheet.getLastRow() === 0) {
    sheet.appendRow(['Date', 'From', 'To', 'Subject', 'Body preview', 'Message ID']);
  }

  const label =
    GmailApp.getUserLabelByName(LABEL_NAME) || GmailApp.createLabel(LABEL_NAME);
  const threads = GmailApp.search(SEARCH_QUERY, 0, MAX_THREADS_PER_RUN);
  const rows = [];

  threads.forEach(function (thread) {
    thread.getMessages().forEach(function (message) {
      rows.push([
        message.getDate(),
        safeText(message.getFrom()),
        safeText(message.getTo()),
        safeText(message.getSubject()),
        safeText(message.getPlainBody().substring(0, BODY_PREVIEW_CHARS)),
        message.getId(),
      ]);
    });
    thread.addLabel(label);
  });

  if (rows.length === 0) {
    return;
  }
  // One write for all rows is much faster than one write per row.
  sheet
    .getRange(sheet.getLastRow() + 1, 1, rows.length, rows[0].length)
    .setValues(rows);
}

// Stops email text that starts with = + - @ from being read as a formula.
function safeText(value) {
  const text = String(value);
  return /^[=+\-@]/.test(text) ? "'" + text : text;
}

// Run this ONCE to make the script check Gmail every 5 minutes.
function createFiveMinuteTrigger() {
  ScriptApp.newTrigger('logEmails').timeBased().everyMinutes(5).create();
}

The script writes all rows in one call. Google's best practices guide says alternating reads and writes is slow, and its example shows about 70 seconds for the inefficient version against 1 second for the batched one.

The safeText function matters. Google's Sheet reference says that a cell whose content begins with = is interpreted as a formula. Without the guard, an email subject starting with = would run as a formula in your sheet.

How do you make it run automatically? (Method 2: add a trigger)

A trigger makes the script run on a schedule without you opening the sheet. You can create one in code or in the editor.

Option A, in code (recommended):

  1. In the function drop-down, choose createFiveMinuteTrigger.
  2. Click Run. Run it only once, or you will create duplicate triggers.

Option B, in the editor:

  1. Click Triggers (the alarm clock icon in the left sidebar).
  2. Click Add Trigger at the bottom right.
  3. Choose logEmails as the function to run.
  4. Choose a time-based event source and a 5-minute interval. Verify this step: the menu may differ, and the exact option names are not spelled out in Google's guide.
  5. Click Save.

Google's installable triggers guide says these triggers run as the user who created them. It also says they do not fire when a file is open in read-only mode, so create the trigger from the account that owns the sheet.

Which method should you choose?

Pick based on how fresh the log must be. The table compares the options in this guide.

OptionCostDifficultyLimitsBest for
Run logEmails by handFreeEasyYou must remember to run itA one-off export of old email
Trigger every 5 minutesFreeEasyCounts toward daily trigger runtimeNear-live logging
Trigger every 15 or 30 minutesFreeEasySlower updates, less quota usedLarge volumes, or low-priority logs
Narrower search queryFreeEasyOnly matching email is loggedInvoices, orders, one sender

For most people, the 5-minute trigger is the right default. Each run that finds nothing new ends almost at once, so it uses little of the daily runtime.

What's free and what costs money?

The method in this guide costs nothing for a personal Google account. As of 12 October 2026, Google's quota page lists these limits for consumer accounts.

LimitConsumer (gmail.com)
Total trigger runtime90 minutes per day
Single execution6 minutes
Triggers per user per script20

Quotas are per user and reset 24 hours after the first request, according to the same page. Google's Sheets guide suggests a database once a dataset approaches 10 million cells, while Drive's help page states up to 20 million cells or 100MB per spreadsheet. The two pages do not explain the gap. At six columns per email, a log of that size would hold over a million emails, so most people will not reach it.

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

"You do not have permission to call GmailApp." This appears if you try to use Gmail from a custom function typed into a cell, such as =myFunction(). Google's custom function guide says custom functions never ask users to authorize access to personal data. Run the script from the editor or a trigger instead.

The script times out. Google lists a 6 minute limit per execution. This script handles 100 threads per run, so a huge inbox is cleared across several trigger runs. Lower MAX_THREADS_PER_RUN if a single run is too slow.

"Exceeded maximum execution time" or a quota error. You have hit a runtime or daily limit. Switch the trigger to every 15 or 30 minutes, or narrow SEARCH_QUERY. Google says exceeding a quota stops the script with an exception, and the quota resets 24 hours after the first request.

The same emails appear twice. You probably created the trigger more than once. Open Triggers in the left sidebar and delete the extra rows.

Nothing is logged. Check that SEARCH_QUERY matches something. Paste the same text into the Gmail search box to see what it returns. Remember that threads already carrying the logged-to-sheet label are skipped on purpose.

A reply never shows up. The script labels whole threads. A later reply in a labelled thread is skipped. See the next section for the trade-off.

Is this allowed?

Yes, for your own mailbox. Apps Script is Google's own tool, and the script only reads your own account.

Three cautions apply. First, Gmail methods need the https://mail.google.com/ scope, per Google's GmailApp reference, which is broad access. Apps Script chooses scopes by scanning your code, and you can review them under Overview in the editor, in the Project OAuth Scopes list.

Second, Google says users may see a warning that the app is unverified. Google's help page defines an unverified app as one that requests a sensitive or restricted scope without going through verification. Google's scopes page says you can use restricted scopes freely for non-public apps, which covers a script you write for yourself.

Third, an email log is a copy of your private mail. Anyone who can open the sheet can read the body previews. Do not share the sheet widely, and do not use the script on a mailbox you do not own. If the mailbox holds customer data, check your workplace's data rules first.

What's still unclear?

The biggest weakness of this method is thread-level de-duplication. Labelling a thread means replies that arrive later are not logged. A more complete version stores each message ID in a column and checks it before writing. That version needs more code, and a long log makes each check slower.

Google's pages also disagree on some numbers. The best practices guide mentions 30 minutes per execution for some Google Workspace accounts, while the quota page lists 6 minutes for both account types. This guide uses 6 minutes, the safe figure for personal accounts. Spreadsheet size is another gap: 10 million cells in the Apps Script guide against 20 million in Drive's help page.

Finally, Google's published guides do not spell out the exact wording of the permission screens or every option name in the trigger dialog. That is why two steps above carry a "Verify this step" note.

Last checked: 12 October 2026.

Frequently asked questions

Can Google Sheets pull in Gmail emails without any code?

Not with a built-in formula. Sheets custom functions cannot use services that need authorization, and Google's documentation says Gmail is one of them. You need either a script that runs from a menu or a trigger, as in this guide, or a separate automation tool. The Apps Script method here is free for personal Google accounts within Google's published daily quotas.

How often can the script check for new email?

A time-driven trigger can run every 1, 5, 10, 15 or 30 minutes, according to Google's ClockTriggerBuilder reference. Each run is a separate execution, so each one counts toward the daily trigger runtime quota. As of 12 October 2026, Google lists 90 minutes per day of total trigger runtime for consumer accounts.

Why did the script stop after logging some emails?

The most common cause is a quota or time limit. Google lists a 6 minute limit per execution and a daily cap on total trigger runtime. This script processes at most 100 threads per run and resumes on the next trigger, so a large inbox is logged over several runs rather than all at once.

Will the script log replies that arrive in a thread I already logged?

No. The script labels a thread after logging it and then skips labelled threads, so a new reply in that thread is not logged. If you need every reply, remove the label from active threads, or change the script to store message IDs in a column and skip IDs it has already written.

Is it safe to give a script access to my Gmail?

The script runs under your own account and you approve its access on a consent screen. Gmail methods in Apps Script require the https://mail.google.com/ scope, which is broad. Only run code you have read, never paste scripts from strangers, and check Project OAuth Scopes under Overview in the editor.

Can I log only some emails, such as invoices?

Yes. The search line in the script accepts the same search operators you type into the Gmail search box. Change the query from in:inbox to something like in:inbox from:billing@example.com or in:inbox subject:invoice, and only matching threads are logged.

Sources

  1. Class GmailApp, Google for Developers (2026-04-13)
  2. Class GmailMessage, Google for Developers (2026-04-13)
  3. Class GmailThread, Google for Developers (2026-04-13)
  4. Class Sheet, Google for Developers (2026-04-13)
  5. Quotas for Google Services, Google for Developers (2026-09-03)
  6. Installable Triggers, Google for Developers (2026-09-03)
  7. Class ClockTriggerBuilder, Google for Developers (2026-04-13)
  8. Best Practices (Apps Script), Google for Developers (2026-09-03)
  9. Authorization for Google Services, Google for Developers (2026-09-03)
  10. Authorization Scopes, Google for Developers (2026-09-03)
  11. Unverified apps, Google Cloud Help (2026-10-12)
  12. Custom Functions in Google Sheets, Google for Developers (2026-09-03)
  13. Extend Google Sheets with Apps Script, Google for Developers (2026-09-03)