google-ads-scripts
Google Ads Scripts
JavaScript that runs in Google Ads (Tools > Bulk actions > Scripts) on the Google Ads API. Selector conditions and reports use GAQL field names (campaign.status, ad_group_criterion.quality_info.quality_score, metrics.cost_micros); Google picks the API version, so scripts do not pin one.
Steps
-
Read with the cheapest tool. Use selectors (
AdsApp.keywords().withCondition(...)) when you will modify the entities you read. UseAdsApp.searchorAdsApp.reportwith a GAQL query when you need metrics theStatsobject lacks (conversion value, ROAS, impression share, quality-score components) or rows across many entities. Done when every metric the script uses comes from a source that actually returns it. -
Filter on the server. Put every filter in
.withCondition()or the GAQLWHERE, add.forDateRange('LAST_30_DAYS')(or another relative range) whenever a condition touchesmetrics.*, and cap with.withLimit(). Done when no JavaScriptifre-filters what a condition could have filtered, and no date is hard-coded. -
Get the units right. Entity methods are in account currency (
Stats.getCost(),Budget.setAmount(50),keyword.bidding().setCpc(1.25)); GAQL is in micros (metrics.cost_micros > 100000000means 100). Done when every/ 1000000divides a GAQL*_microsvalue and nothing else. -
Gate every mutation. Ship with
DRY_RUN = true, clamp each change (minimum and maximum bid, maximum percentage change per run), skip shared budgets unless intended (budget.isExplicitlyShared()), and record what changed to a Sheet or email. Done when a dry run logs exactly the changes a live run would make. -
Fit the 30-minute limit. Collect the IDs to change first, then mutate; fetch entities in bulk with
.withIds([...])(10,000 per selector). For manager accounts useAdsManagerApp.accounts().executeInParallel(...)(50 accounts per call). Done when the run time is bounded by.withLimit()or batch size, not account size.
Quick start
Pause keywords with a low quality score and real spend: