Key takeaways
- Batch reads and writes. One request for a range beats a hundred for single cells.
- Apps Script runs stop at 6 minutes, custom functions at 30 seconds, and daily mail and fetch quotas differ sharply by account type.
- Simple triggers cannot send mail, and script edits do not fire onEdit.
- Give rows stable IDs and keep reports separate from raw data.
- Move to a database once volume and formulas outgrow the sheet.
Google Sheets is the most popular ‘database’ in small business, and for good reason: everyone knows it. It is also the place where automations quietly hit quotas. A script that handled ten rows now times out at ten thousand, a daily email report stops at 100 messages, and a custom function shows an error nobody can explain.
Here are the limits that matter most, what breaks when you reach them, and the design that keeps your sheet-based automation healthy.
The Sheets API quota
If you read and write through the Sheets API, you share a quota. As we read the documentation, you get about 300 read requests and 300 write requests per minute for each project, and about 60 of each per minute for each user. Those numbers sound generous until a loop writes one cell at a time.
- Batch your writes. Send one request that updates a whole range instead of a hundred requests that each update a cell.
- Read once, process in memory. Pull a range into a list, work on it, and write it back.
- Back off on quota errors. Wait and retry, with growing gaps, instead of hammering the API.
Apps Script runtime limits
| Limit | Typical value | What it breaks |
|---|---|---|
| Single script run | 6 minutes | Long loops over large sheets stop partway. |
| Custom function run | 30 seconds | Slow lookups return an error instead of a value. |
| Daily trigger runtime | About 90 minutes (consumer accounts), about 6 hours (Workspace) | A busy automation stops running late in the day. |
| Emails sent per day | About 100 (consumer), about 1,500 (Workspace) | Reports and alerts stop going out. |
| URL fetch calls per day | About 20,000 (consumer), about 100,000 (Workspace) | Integrations that call other services pause. |
Quotas reset daily and differ by account type. Look up the current values for your account before you design around them.
Triggers: what runs, and what doesn’t
- Simple triggers cannot send mail. A simple
onEdittrigger runs with limited authorization, so it cannot call services that need permission, such as sending email. Use an installable trigger when you need those. - Script edits do not fire
onEdit. If your script writes to a cell, that change does not run anotheronEdittrigger. Only human edits and some API changes do. - Triggers are not instant. Time-driven triggers run within a window, not at an exact second.
- Several edits can arrive together. Pasting a block of cells triggers one event for the range. Your handler must loop over it.
Because a script’s own writes do not fire onEdit, an onEdit that updates a sibling column cannot trigger itself forever. If you need a follow-on action, call a function directly.
Spreadsheet size
A spreadsheet has an overall cell limit, and formulas that reference whole columns, volatile functions and many cross-sheet lookups make big sheets slow long before they reach it. If your sheet is the system of record, plan for what it will look like in a year. A database is better than a sheet once you pass tens of thousands of rows with many formulas.
A healthy sheet-based automation
- Give every row a stable ID in its own column.
- Keep raw data on one tab and reports on another, so formulas never fight with writes.
- Write in batches through the API, not cell by cell.
- Run long jobs as small time-driven chunks that store their progress.
- Send emails in a digest rather than one per row.
- Log errors to a tab where a person will see them.
- Protect formula columns so people and scripts cannot overwrite them.
A short checklist
- Writes batched into ranges
- Reads done once per run
- Runtime under 6 minutes per run
- Email volume under the daily limit, or digested
- Installable trigger used when mail or other services are needed
- Row IDs in a dedicated column
- Errors logged to a visible tab
- Quotas checked for the account type