Basics
Google Apps Script quotas and limits
Google caps how long one run may take and how much your scripts may do in a day. Knowing the caps changes how you write anything that touches a lot of rows.
1 of 8
What a quota is
A quota is a cap Google puts on how much your scripts may do. There are two kinds. A limit applies to one run, such as the six minutes a single execution may take. A daily quota applies to everything your scripts do in a day, such as the number of email recipients.
Quotas are counted per Google account, not per script. Five scripts sending email from the same account share one daily email allowance between them.
Nothing is charged and nothing breaks for good. When you go over, that run stops with an error. Daily quota windows reset about 24 hours after the first request that used that quota, not 24 hours after you exhaust it.
2 of 8
A personal account and a work account get different numbers
A consumer account is a free address ending in gmail.com. A Google Workspace account is one your employer or your school pays for, usually at your own domain.
Workspace accounts get much larger daily quotas. Email is where people notice first: 100 recipients a day on a consumer account against 1,500 on Workspace, at the time of writing.
What counts is the account that owns the run. A trigger runs as the person who created it, so a script running on a colleague trigger spends that colleague quota, not yours.
3 of 8
The numbers, at the time of writing
Google changes these from time to time, so treat the list as a guide and check the official page before you build something that depends on an exact number. It is at https://developers.google.com/apps-script/guides/services/quotas and it shows the consumer and the Workspace figures side by side.
- Script runtime: 6 minutes for one execution.
- Custom function runtime: 30 seconds for one call from a cell.
- Triggers total runtime: 90 minutes a day on a consumer account and 6 hours a day on Google Workspace.
- Email recipients a day: 100 on a consumer account, 1,500 on Google Workspace.
- URL Fetch calls a day: 20,000 on a consumer account, 100,000 on Google Workspace.
- Properties reads and writes a day: 50,000 on a consumer account, 500,000 on Google Workspace.
- Triggers: 20 per user per script.
- Simultaneous executions: 30 per user.
4 of 8
The six minute limit
One execution gets six minutes at the time of writing. Past that, Apps Script stops it and records "Exceeded maximum execution time". Everything already written to the sheet or already sent stays done, so a run stopped halfway leaves half the job finished.
A custom function typed into a cell gets 30 seconds, which is far less, because a sheet can call hundreds of them at once. All of your triggers together get 90 minutes a day on a consumer account or 6 hours on Google Workspace, so a five-minute job cannot run every hour around the clock on a consumer account but can fit within the Workspace runtime allowance.
The usual first fix is doing less work per row.
5 of 8
Reading in bulk protects you from more than slowness
Every call to a Google service is a round trip from the script to Google servers and back. A loop that calls getValue() once per cell across a thousand rows makes a thousand round trips, and that on its own can use the whole six minutes.
One getValues() call reads those thousand rows in a single trip. One setValues() writes the answers back in one more. The same script then finishes in seconds and leaves your quotas alone.
The idea carries over to the other services. Search Gmail once and loop over the results. Fetch one web address that returns many records instead of one address per record.
6 of 8
Split a long job into batches
When there is more work than six minutes allows, do part of it per run and remember where you stopped. PropertiesService gives you a small store of text that survives between runs, so it can hold a resume-from-row marker. You can see what is stored under Project Settings, in the Script Properties table.
The loop below watches the clock. Once four minutes have gone by it stops early, saves the row number to start from next time, and returns. A time driven trigger calls the function again later, it reads the marker, and it carries on where it left off.
Stop well before the limit. Four minutes leaves room for a slow last row and for the writing at the end. When the marker passes the last row, the function clears it so the next run starts fresh.
function processCustomersInBatches() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Customers');
if (!sheet) throw new Error('Add a Sheet tab named Customers before running this function.');
const properties = PropertiesService.getScriptProperties();
const firstDataRow = 2;
const startRow = Number(properties.getProperty('nextRowToProcess') || firstDataRow);
const lastRow = sheet.getLastRow();
if (startRow > lastRow) {
properties.deleteProperty('nextRowToProcess');
Logger.log('Every row is done. The next run starts again from row ' + firstDataRow + '.');
return;
}
const values = sheet.getRange(startRow, 1, lastRow - startRow + 1, 2).getValues();
const stopAfterMilliseconds = 4 * 60 * 1000;
const startedAt = Date.now();
let index = 0;
while (index < values.length) {
const [name] = values[index];
Logger.log('Handling row ' + (startRow + index) + ': ' + name);
index += 1;
if (Date.now() - startedAt > stopAfterMilliseconds) break;
}
const nextRow = startRow + index;
properties.setProperty('nextRowToProcess', String(nextRow));
Logger.log('Handled ' + index + ' rows. The next run starts at row ' + nextRow + '.');
} 7 of 8
How a limit looks in Executions
Open the left sidebar of the editor and click Executions, the icon that looks like a list with a clock. Every run of every function is listed there with its status, when it started, and how long it took.
A run that hit the six minute limit shows Timed out. A run stopped by a quota shows Failed, and opening the row shows the message. "Exceeded maximum execution time" is the six minute limit. "Service invoked too many times for one day" is a daily quota, and the word after the colon names which one.
The duration column is your early warning. A run that has crept up to four or five minutes will start failing as soon as there is a little more data.
8 of 8
What to do when you hit a limit
Most scripts that run out of time are doing a small amount of real work with far too many service calls around it.
- Wait for the reset. A daily quota window clears about 24 hours after its first request, not after it filled up, and there is no way to buy more or to clear it early.
- Do less per run. Send to fewer recipients, or handle a few hundred rows rather than all of them.
- Split the work across runs with a saved marker, as in the batch example above.
- Cut the round trips. One getValues() and one setValues() in place of a call per cell.
- Mark finished rows with a status column, so a second run does not repeat work that already cost you quota.
- Check which account is running it. A Workspace account has far more headroom than a personal one for the same script.
Remember
The short version
- One run gets 6 minutes, a custom function gets 30 seconds, and all triggers together get 90 minutes a day on a consumer account or 6 hours on Google Workspace, at the time of writing.
- A free Gmail account may email 100 recipients a day and a Google Workspace account 1,500, counted per account rather than per script.
- A trigger spends the quota of whoever created it, not the quota of whoever is watching.
- One getValues() and one setValues() beat a call per cell, and that alone keeps most runs inside the time limit.
- Check the current figures on the Apps Script quotas page before you rely on any of them.
Keep going
Put it to work
- Task guide Send an email for each row in a Google Sheet
Use one row per person, then let the script send only the rows that have not already been marked as sent.
- Task guide Run an Apps Script function every day
Run the setup function once, then Apps Script calls the reminder function each day around your chosen hour.
- Task guide Store settings with PropertiesService
PropertiesService.getScriptProperties() stores small key-value settings with the Apps Script project. Save a setting once with setProperties(), then read it on every run with getProperty().
- Task guide Move finished rows to an Archive tab
Rows with the status Done are copied to the bottom of an Archive tab and then removed from Tasks. This one deletes rows, so test it on a copy of the file first.
- Basics Common Apps Script errors and what they mean
Apps Script error messages name the failure directly, from TypeError: Cannot read properties of null (reading 'getRange') to ReferenceError and quota limits. Find the exact message below for the cause and fix.
- Basics Google Apps Script triggers explained
A Google Apps Script trigger runs a function when an event happens, without pressing Run. Simple triggers like onEdit and onOpen need no setup, while installable triggers run on a schedule or form submit using the OAuth scopes approved by their owner.
- Basics Execution log, Logger.log, and debugging
Most Apps Script debugging is printing a value with Logger.log or console.log and reading the error line. This page shows where the output goes and how to trace a failure.
- Basics Custom functions in Google Sheets
A Google Sheets custom function runs in a silent sandbox with its own rules: 30-second timeouts, raw 2D arrays instead of Range objects, no personal services, and no cell edits.
- Diagnostic Service call inside a loop
How moving getValues() and setValues() outside a row loop keeps long spreadsheet runs under the 6-minute execution limit.
- Book chapter Debug executions and recover from limits
Step through a request audit, trace failures in execution history and logs, and recover bounded work without blind retries.