fx Triggers & Automation

Quotas, execution limits and workarounds

⏱ 12 min

What you'll learn

  • The limits that matter most in day-to-day scripts
  • How to process big jobs in chunks that survive the 6-minute limit
  • Caching, locking, and checking your email quota

Concept

The limits you'll actually hit

Google publishes the full list on its Quotas for Google Services page, and the numbers change from time to time — always check there for current values. These are the ones that matter most:

Limit Personal Gmail account Google Workspace account
Run time per execution ~6 minutes ~6 minutes
Custom function run time ~30 seconds ~30 seconds
Email recipients per day ~100 ~1,500
Total trigger run time per day ~90 minutes ~6 hours
Triggers per user per script 20 20

The email and 6-minute limits are the ones most beginners run into.

Check email quota before sending

function sendIfQuota(recipients) {
  const left = MailApp.getRemainingDailyQuota();
  if (left < recipients.length) {
    console.warn(`Only ${left} emails left today; need ${recipients.length}.`);
    return false;
  }
  // ... send ...
  return true;
}

Beating the 6-minute limit: work in chunks

Save your position, stop before time runs out, and continue on the next run.

const MAX_MS = 5 * 60 * 1000; // stop after 5 minutes, leaving a safety margin

function processInChunks() {
  const start = Date.now();
  const props = PropertiesService.getScriptProperties();
  let nextRow = Number(props.getProperty('NEXT_ROW') || 2);

  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Customers');
  const lastRow = sheet.getLastRow();

  while (nextRow <= lastRow) {
    if (Date.now() - start > MAX_MS) break;

    const batchSize = Math.min(50, lastRow - nextRow + 1);
    const rows = sheet.getRange(nextRow, 1, batchSize, 3).getValues();
    rows.forEach(r => {
      // ... slow work per row, e.g. an API call ...
    });

    nextRow += batchSize;
    props.setProperty('NEXT_ROW', String(nextRow)); // save progress after each batch
  }

  if (nextRow > lastRow) {
    props.setProperty('NEXT_ROW', String(nextRow)); // retain completion cursor
    console.log('All rows done');
  } else {
    console.log(`Paused at row ${nextRow}; will continue on next run`);
  }
}

Pair this with a time-driven trigger every 10–15 minutes. When the job finishes, the cursor stays after the last processed row; later runs only process newly appended rows. Delete NEXT_ROW manually only when you intend to restart the whole job.

Cache slow lookups

If many runs fetch the same data (e.g. today's gold rate from an API), cache it:

function getRate() {
  const cache = CacheService.getScriptCache();
  const cached = cache.get('GOLD_RATE');
  if (cached) return Number(cached);

  const rate = fetchRateFromApi();             // slow call (your own function)
  cache.put('GOLD_RATE', String(rate), 3600);  // keep for 1 hour (max 6 hours)
  return rate;
}

Prevent two runs clashing

If a trigger and a user can run the same function at the same time (e.g. both appending invoice numbers), use a lock:

function nextInvoiceNumber() {
  const lock = LockService.getScriptLock();
  lock.waitLock(10000); // wait up to 10 s
  try {
    const props = PropertiesService.getScriptProperties();
    const n = Number(props.getProperty('LAST_INVOICE') || 0) + 1;
    props.setProperty('LAST_INVOICE', String(n));
    return n;
  } finally {
    lock.releaseLock();
  }
}

Other speed tips

  • Batch reads/writes (Module 2) solve most "too slow" problems.
  • Avoid SpreadsheetApp.flush() inside loops.
  • Don't open the same file again and again — open once and pass it around.

Key points

  • Plan for ~6 minutes per run and daily email limits; check Google's quota page for current numbers.
  • Big jobs: save progress in Script Properties and continue on the next trigger.
  • CacheService for repeated lookups, LockService for shared counters.

Exercises

mediumPractice 1

Modify processInChunks so that, when all rows are done, it also deletes its own time-driven trigger.

Quiz

Roughly how long can one execution run? a) 30 seconds b) About 6 minutes c) 1 hour
About 6 minutes
Which service stops two runs from creating the same invoice number? a) CacheService b) LockService c) MailApp
LockService
Quotas, execution limits and workarounds · Google Apps Script | ExcelWalaa