GAQL promises all the data; reality imposes limits: on time, on quota, on privacy, on freshness and on versions. A process that does not know them fails on a Tuesday at 3am with nobody watching. This lesson is the list of limits and the design that survives them.
Google Ads scripts limits
| Limit | Value | Consequence |
|---|---|---|
| Time per run | 30 min (60 for some parallel manager-account runs) | Long jobs get cut off: you have to split them into batches and resume |
| Frequency | One scheduled run per hour per script | There is no "every 10 minutes" |
| Calls to external services (UrlFetch) | A daily quota per user: ~20,000 on Gmail, ~100,000 on Workspace | Scripts that call an AI or a server burn quota; whoever runs them on Workspace has five times as much |
| Spreadsheets | Cell limits and Sheets call limits | Write in blocks, not cell by cell |
| A daily sending quota | Grouped alerts, not one per event | |
| Concurrent runs | Limited per account | Several scripts at the same hour tread on each other |
API limits
- Operations and rows per request: large but finite; queries are paged.
- Daily quotas per developer token (basic vs standard access): a high volume of queries requires standard access.
- Mutations: limits per request and strict validation (budgets in micros as multiples, targets within ranges).
Data that does not exist, or arrives late
- Privacy threshold: low-volume search terms (and AI Max combinations) do not appear; the sum of what you can see is not the total.
- Metric lag: today's metrics are incomplete; conversions arrive late; some metrics (IS, quality) update hours or a day behind. The rule: query up to yesterday and re-query the last few days on every run (incremental re-download with overlap).
- QS history: it does not exist in the API; only the current value.
- Auction insights: limited access.
- PMax: placements with impressions only; search terms by category.
API versions
The Google Ads API ships new versions several times a year and retires old ones after a while; fields and resources get renamed or disappear. Google Ads scripts use a version managed by Google (less maintenance), but fields still change. The design answer: queries with the minimum fields, and a compatibility check (preview) after every version notice.
Designing processes that survive
- Batches and resuming: split the work (by account, by campaign, by date range), store a cursor (where it got to) in the sheet or in the script's properties, and resume on the next run. Heavy work once a day per account; light work every hour.
- Frozen date windows: work out the range once at the start of the job and store it; if the job spans several runs, all of them use the same range (otherwise the batches will not reconcile).
- Incremental download with overlap: every day, the last N days (3-7) are downloaded again and replace what was stored: that is how late conversions get in.
- Retries with a wait on transient API errors; and if it fails, it does not touch anything on writes.
- Block writes to sheets; block reads of configuration.
- A consumption meter: count external calls per run and per account, and report them (so you do not hit the quota at the end of the month).
- Diagnostics: a tab with the trace of the last run and errors highlighted; without it, the 3am failure is invisible.
- Large accounts: sampling or pre-aggregation (GAQL lets you sort and limit: the 2,000 highest-spending search terms instead of 50,000).
Specific traps
- Querying
segments.datewith no range → error. - Selecting ten segments "just in case" → millions of rows → time runs out.
- IDs stored as numbers in Sheets → rounded → broken joins.
- Micros added up as if they were euros.
- Forgetting the account's time zone (
customer.time_zone) when working out "yesterday". - A script that calls the AI once per row → UrlFetch quota exhausted by midday: batch them.
💡 Ninja trick: several of these designs are standard in the Suite today precisely because they hit the limit first: the Agent has a daily gate (heavy work once a day per account, pending items first, light KPIs every hour) and a rotating cursor for resuming; the Shield's Smart Mode downloads RAW data incrementally with a frozen window; every script carries a UrlFetch meter per account and a Diagnostics tab with the trace. And the analyses that do not fit into 30 minutes (Site Analyzer across thousands of domains) moved to a server of our own.
What you should remember
- Scripts: 30 min, one per hour, a UrlFetch quota (Workspace ×5), Sheets and email in blocks.
- Data: privacy threshold, lag (query up to yesterday with overlap), no QS history, PMax limits.
- API versions change: minimum fields and a compatibility check.
- Design: batches with a cursor, frozen windows, incremental with overlap, retries, if it fails it does not touch, a meter, diagnostics.