Google Ads scripts guide: when to use them and how to test them
A Google Ads scripts guide: rules or scripts, current limits, GAQL, a starter script that allows for conversion delay and the steps to test a script first.
13 min read

Google Ads scripts are small JavaScript programs that run inside your Google Ads account. They can read account data and change campaigns, ad groups and keywords. They can also email the result or write it to Google Sheets. You create them in Google Ads from the Tools menu, under Bulk actions, then Scripts.
But a script is not the right tool for every automation job. Pausing a campaign on a single condition only needs an automated rule, while a product that manages hundreds of accounts needs the Google Ads API. This guide first sorts out which job belongs to which tool. It then covers how a script is built, the current limits and how to test a script before you run it in your account. It ends with a starter script written with the methods in Google's documentation and a checklist for spotting outdated scripts.
Rules, scripts or the API?
For bulk management without code, Google points to automated rules, bulk uploads and Google Ads Editor. Scripts suit advertisers who know JavaScript or work with someone who does and have a large account. The table below sums up the choice by need.
Need: Pause, enable or change budget on a simple condition
- Right tool
- Automated rule
- Why
- No code, and changes can be undone
Need: Bulk edits without code
- Right tool
- Google Ads Editor, bulk uploads
- Why
- For one-off large changes
Need: Conditional logic, custom reports, email alerts
- Right tool
- Script
- Why
- You write any rule in JavaScript
Need: Decisions from outside data (stock, weather, your database)
- Right tool
- Script
- Why
- It can send requests to outside addresses
Need: Many accounts, your own software, an always-on system
- Right tool
- Google Ads API
- Why
- Script limits are not enough
| Need | Right tool | Why |
|---|---|---|
| Pause, enable or change budget on a simple condition | Automated rule | No code, and changes can be undone |
| Bulk edits without code | Google Ads Editor, bulk uploads | For one-off large changes |
| Conditional logic, custom reports, email alerts | Script | You write any rule in JavaScript |
| Decisions from outside data (stock, weather, your database) | Script | It can send requests to outside addresses |
| Many accounts, your own software, an always-on system | Google Ads API | Script limits are not enough |
The real question is whether the job fits a rule's condition-and-action shape. Jobs that combine several data points, pull in outside information or produce a custom report need a script. Jobs tied to a single threshold are safer as rules, because the most recent changes made by rules can be undone. Scripts have no such built-in undo.
Timing is a separate operational note. Rules usually run within two hours after their conditions are triggered. Scripts can be scheduled hourly, daily, weekly, monthly or on demand. That does not mean a script always reacts faster, because a script also runs only at its scheduled time and on the data it has.
How a script works: selectors and GAQL
Every script starts with a main function and reaches the account through the AdsApp object.
There are two ways to reach entities in the account. The first is selectors. You filter a collection such as AdsApp.campaigns() with withCondition, then loop over the results. The second is reports. You run a query written in Google Ads Query Language (GAQL) with AdsApp.search().
The reporting infrastructure is backed by the Google Ads API and uses GAQL to set the fields, metrics and conditions. Fields in search() results come back in lowerCamelCase, even though the query writes them with underscores. You write metrics.cost_micros in the query and read row.metrics.costMicros in the result.
If you need statistics, use reports rather than selectors. Reports perform better for large queries and do not hit normal fetching quotas. Google's best practices page also recommends search() over the older report() method.
Watch the units. metrics.cost_micros comes in micros, so divide it by 1,000,000 to get the account currency. Conversion value, metrics.conversions_value, is not in micros. If you calculate return on ad spend (ROAS) in a script, divide conversion value by the cost converted from micros. You can compare the result with the ROAS calculator.
The current limits
The values below come from Google's limits page and show the state on October 30, 2026. Google says these limits can change at any time without warning, so scripts should handle errors.
Limit: Run time
- Value
- 30 minutes
- Note
- Changes made before the cut-off are applied
Limit: Manager account script
- Value
- Up to 60 minutes
- Note
- Only with executeInParallel and a callback
Limit: Accounts processed in parallel
- Value
- 50 accounts
- Note
- 30 minutes per account
Limit: Results from one iterator
- Value
- 50,000 by default
- Note
- Can be changed with withLimit
Limit: IDs in withIds
- Value
- 10,000
- Note
- Per selector
Limit: Reports
- Value
- No entity limit
- Note
- Use reports for statistics
Limit: Log output
- Value
- 100 kilobytes
- Note
- Truncated after that
Limit: Authorized scripts
- Value
- 250
- Note
- Authorized with OAuth2
| Limit | Value | Note |
|---|---|---|
| Run time | 30 minutes | Changes made before the cut-off are applied |
| Manager account script | Up to 60 minutes | Only with executeInParallel and a callback |
| Accounts processed in parallel | 50 accounts | 30 minutes per account |
| Results from one iterator | 50,000 by default | Can be changed with withLimit |
| IDs in withIds | 10,000 | Per selector |
| Reports | No entity limit | Use reports for statistics |
| Log output | 100 kilobytes | Truncated after that |
| Authorized scripts | 250 | Authorized with OAuth2 |
This table corrects two mistakes common in both Turkish and English guides.
The 50,000 results are a default, not a hard ceiling. And 250 is the number of scripts that can be authorized, not the number that can run at once. Google's pages word this limit differently: the limits page says per account, while the overview says per user.
A starter script: flagging spend with no conversions
A common account question is when keywords that spend without converting should be reviewed. The threshold for that decision comes from margin. The performance marketing metrics guide explains how break-even cost per acquisition (CPA) is calculated. But if the threshold equals break-even CPA, you will often flag keywords that actually work.
Hypothetical calculation: suppose a keyword's true CPA equals break-even and its conversions arrive independently. The chance of spending a given multiple of break-even CPA with no conversion is then roughly as below. This is a Poisson approximation, and it holds for one keyword in one evaluation.
Spend: 1× break-even CPA
- Chance of no conversion
- About 37%
- What it means
- A third of healthy keywords can get flagged
Spend: 2×
- Chance of no conversion
- About 14%
- What it means
- Still frequent false alarms
Spend: 3×
- Chance of no conversion
- About 5%
- What it means
- One in twenty under these assumptions
| Spend | Chance of no conversion | What it means |
|---|---|---|
| 1× break-even CPA | About 37% | A third of healthy keywords can get flagged |
| 2× | About 14% | Still frequent false alarms |
| 3× | About 5% | One in twenty under these assumptions |
This table is not a rule for pausing keywords. It sets the threshold for a review alert. If you check hundreds of keywords every day, a few healthy keywords can reach the list every day even at a 5% chance. The number is not exact, because the daily windows overlap and not every keyword meets the model's conditions. In real accounts, conversions also vary by season and by day.
Two more things distort the threshold. The first is conversion delay. Google Ads conversion tracking data can take up to 3 hours to be ready under last click and up to 15 hours under other attribution models, according to Google's data freshness page. Customers may also buy days after they click. If you include the last few days, you flag keywords whose conversions have not arrived yet. So check how long clicks take to convert in your account and leave that many days out.
The second is what a conversion means. A form fill and a sale are not judged by the same CPA. The conversion the script counts should be the same business result you used to set the threshold. If the account tracks several conversions with different values, be clear which one the threshold is based on.
The script below leaves out the last 7 days and looks at the 30 days before them. It finds keywords that had no conversions in that window and spent more than the threshold. It only looks at keywords whose campaign, ad group and keyword are all enabled. It writes every line to the log and, outside preview mode, sends the result by email. It changes nothing in the account.
In the example, break-even CPA is $25 and the threshold is three times that, $75. The 7 excluded days are also an example value. Change them to match how long conversions take in your account. For example, a run on November 5 queries September 30 to October 29, so the 7 days from October 30 to November 5, today included, stay out.
// Settings
const THRESHOLD = 75; // three times break-even CPA (here $25 × 3)
const DAYS = 30; // days evaluated
const EXCLUDED_DAYS = 7; // recent days whose conversions may not have arrived
const RECIPIENT = 'you@example.com';
function main() {
const account = AdsApp.currentAccount();
const timeZone = account.getTimeZone();
const dayMs = 24 * 60 * 60 * 1000;
const now = new Date();
const end = Utilities.formatDate(
new Date(now.getTime() - EXCLUDED_DAYS * dayMs), timeZone, 'yyyy-MM-dd');
const start = Utilities.formatDate(
new Date(now.getTime() - (EXCLUDED_DAYS + DAYS - 1) * dayMs), timeZone, 'yyyy-MM-dd');
const query =
'SELECT campaign.name, ad_group.name, ad_group_criterion.criterion_id, ' +
'ad_group_criterion.keyword.text, ad_group_criterion.keyword.match_type, ' +
'metrics.cost_micros ' +
'FROM keyword_view ' +
`WHERE segments.date BETWEEN '${start}' AND '${end}' ` +
"AND campaign.status = 'ENABLED' " +
"AND ad_group.status = 'ENABLED' " +
"AND ad_group_criterion.status = 'ENABLED' " +
'AND metrics.conversions = 0 ' +
`AND metrics.cost_micros > ${THRESHOLD * 1000000}`;
const lines = [];
for (const row of AdsApp.search(query)) {
const keyword = row.adGroupCriterion;
const cost = Number(row.metrics.costMicros) / 1000000;
lines.push(
`${row.campaign.name} > ${row.adGroup.name} > ${keyword.keyword.text} ` +
`[${keyword.keyword.matchType}, ${keyword.criterionId}]: ${cost.toFixed(2)}`);
}
const header = `${account.getName()} (${account.getCustomerId()}), ` +
`${start} / ${end}, threshold ${THRESHOLD} ${account.getCurrencyCode()}`;
console.log(`${header}: ${lines.length} keywords`);
lines.forEach((line) => console.log(line));
if (lines.length > 0 && !AdsApp.getExecutionInfo().isPreview()) {
MailApp.sendEmail(RECIPIENT, 'Spend with no conversions',
`${header}\n\n${lines.join('\n')}`);
}
}This example uses the metrics.conversions metric and does not select a specific conversion action. The conversions included in that metric should match the business result you used to set the CPA threshold.
The script uses the AdsApp.search loop from Google's getting started page and the method on the dates page. Dates are calculated in the account's time zone, because scripts default to America/Los_Angeles whatever the account's time zone is. The fields in the query appear in the Google Ads API's keyword_view field list.
The script was not run in a real account for this guide. Preview it in your own account first and check the lines it writes to the log. When you set the threshold, you can check cost per click (CPC) with the CPC calculator and conversion rate with the conversion rate calculator.
Before you use it in production
The starter script raises an alert, but it does not manage the alert. Running it daily avoids an email every hour, but it does not stop the same keyword reappearing the next day. If you reviewed a keyword and chose to keep it, mark it with a label and exclude it from the query, or keep a sheet of keywords already sent.
"No results" and "the script did not run" are not the same. When the script finds no keywords it sends no email, and that silence can look like the silence of a broken script. Check that it ran on the Script history page, or use a version that sends a short "no results" email.
How to test a script: preview and safety
Preview mode is not as safe as it looks. In preview, scripts make no changes to campaign data, but only for calls that go through AdsApp. Other services run as normal, so even in preview, email goes out and Sheets get written. That is why the script above checks isPreview() before it sends email.
If you write a script that makes changes, first run a version that only writes the changes to the log, then limit it to one campaign. Do not count on an undo button like the one rules have. Before you change a budget or a bid, write its old value to a sheet so the same script can reverse the change if needed.
Scripts run on your behalf. They are independent entities that act for the user and can run when the user is not signed in. If the person who authorized a script loses access to the account, the script stops and someone with access must reauthorize it. Scripts can stop quietly for this reason when an agency changes or someone leaves the team.
Watch out with Performance Max, Shopping and video campaigns
What a script calls "all campaigns" is not always all campaigns. According to the limits page, the standard campaign selector AdsApp.campaigns() filters out video and Shopping campaigns. A script that checks budgets or status can report that all is well without ever seeing them.
Performance Max campaigns come from a separate collection, performanceMaxCampaigns(), and have no ad group or ad objects you can see. Asset groups can't be created in scripts, but assets can be added to or removed from an existing asset group. Creating a Performance Max campaign is only possible with mutate. Performance Max has no keywords, so the starter script above does not cover these campaigns.
Be careful with older scripts that change manual bids under automated bid strategies. If a keyword's bid strategy does not use max cost-per-click bids, a setCpc change may not affect actual bidding, depending on the strategy in use.
Running scripts from a manager account
Agencies and teams that manage several accounts can run a script from a manager account. In manager account scripts, accounts are reached through AdsManagerApp.accounts(), and up to 50 accounts can be processed at once with executeInParallel. Each account then gets 30 minutes, and each account can return up to 10 MB of data.
If you define a callback function that runs after all accounts finish, the script's total time rises to 60 minutes. With more than 50 accounts, split them into groups with a label or a condition and run each group separately. Bulk actions owned by the top-level manager are visible only to that account, not to sub-manager or client accounts. Someone working in a client account may not see a script that runs on it.
Scripts you did not write yourself
How to spot an outdated script
Many scripts you find online were written for an older version of Google Ads. Some scripts still recommended in Turkish and English guides no longer run, or run on the wrong data. Before you copy a script, look for these signs.
- It uses the names
AdWordsApporMccApp. Current documentation uses onlyAdsAppandAdsManagerApp. - Its reports use old names such as
KEYWORDS_PERFORMANCE_REPORT. This older query language still technically works but is not recommended. - It bids on average position. In a February 2019 announcement, Google said it would sunset average position in September that year and pointed to top and absolute top impression metrics instead.
- It asks you to make the linked Google Sheet public and editable. Sharing it with the account that runs the script is enough.
Google's ready-made scripts
Before you write your own script, Google's ready-made solutions can save time. The table below lists them with the schedule Google suggests for each.
Solution: Account Summary Report
- What it does
- Writes an account summary to Sheets
- Suggested schedule
- Daily, 3am or later
Solution: Account Anomaly Detector
- What it does
- Flags unusual changes
- Suggested schedule
- Hourly
Solution: Ad Performance Report
- What it does
- Reports ad performance
- Suggested schedule
- Weekly, Mondays
Solution: Common Negative List
- What it does
- Applies a shared negative list
- Suggested schedule
- Daily or hourly
Solution: Flexible Budgets
- What it does
- Spreads budget over a period
- Suggested schedule
- Daily, just after midnight
Solution: Link Checker
- What it does
- Finds broken landing page links
- Suggested schedule
- Hourly
| Solution | What it does | Suggested schedule |
|---|---|---|
| Account Summary Report | Writes an account summary to Sheets | Daily, 3am or later |
| Account Anomaly Detector | Flags unusual changes | Hourly |
| Ad Performance Report | Reports ad performance | Weekly, Mondays |
| Common Negative List | Applies a shared negative list | Daily or hourly |
| Flexible Budgets | Spreads budget over a period | Daily, just after midnight |
| Link Checker | Finds broken landing page links | Hourly |
The suggested time for the Account Summary Report shows an important detail: Google Ads statistics may be delayed by up to three hours. A daily report that runs at midnight can miss the last hours of the previous day. Link Checker finds links that return errors on landing pages. Pages that work but do not convert are a separate job, covered in the conversion rate optimization guide. The funnel analysis calculator shows at which step visitors drop off between the landing page and checkout.
Before you run a script written with AI
AI tools make writing scripts easier, but they often produce old method names, fields that do not exist or the wrong units. Before you run a script in your account, follow these steps.
- Look up every object and method name in Google's reference documentation. Do not use a method you cannot find.
- Check every field in the GAQL query against the Google Ads API field list, and see which fields come in micros.
- Run the script in preview, and keep in mind that preview still sends email and writes to Sheets.
- Run the part that makes changes so it only writes to the log first, then limit it to one campaign.
- Write the old value of everything you change to a sheet, so you can reverse the change if needed.
- If you want to measure the script's effect, keep a comparison group where you do not apply the change. The control group guide explains how to set one up.
Scripts are a strong tool where rules fall short. When you add one to your account, think of it as a teammate making decisions for you rather than a piece of code. You need to know what it does, which data it reads, what it cannot see and when its alerts go wrong.

Related tools
What to read next
November 14, 2026Landing page examples: five pages and the principles behind themFive landing page examples, from Shopify to Apple and Waze: what each page shows, the NN/g and Google principles behind it and how to test one element.
November 14, 2026How to get more leads: sources, offers and lead qualityTo get more leads that pay off, choose sources and offers by the qualified leads and sales they bring, then compare channels over the same observation window.
November 5, 2026How to do a Google Ads audit for Search and Performance MaxA seven-phase Google Ads audit for Search and Performance Max that starts with measurement and ranks findings by budget affected and strength of evidence.