Every script below is ready to paste into the editor. The thresholds are sensible starting points, not gospel — a $5 t-shirt niche and a $30 hoodie niche need different numbers. Always preview first, and start new rules in Review mode.
Pause bleeders
The classic first rule: keywords with plenty of clicks and nothing to show for them.
FOR EACH keyword IN LAST 30 DAYS:
IF keyword.clicks >= 25 AND keyword.orders = 0:
keyword.pause()
keyword.note("{clicks} clicks, {spend:money} spent, 0 orders")
END
Bid down high-ACOS keywords (with floor and cool-down)
Cuts bids 15% where ACOS runs hot, never below $0.05, and waits a week between cuts so changes can take effect before being judged.
FOR EACH keyword IN LAST 30 DAYS:
IF keyword.acos > 45% AND keyword.orders >= 1 AND DAYS_SINCE_CHANGE("bid") > 7:
keyword.setBid(MAX($0.05, keyword.bid * 0.85))
keyword.note("ACOS {acos:percent} over target")
END
Bid up proven winners (with ceiling)
FOR EACH keyword IN LAST 30 DAYS:
IF keyword.orders >= 3 AND keyword.acos < 20% AND DAYS_SINCE_CHANGE("bid") > 7:
keyword.setBid(MIN($1.20, keyword.bid * 1.10))
keyword.note("{orders} orders at {acos:percent} ACOS — scaling")
END
Scale budgets on performance
Raises budgets on campaigns that convert efficiently, trims campaigns that spend without selling.
FOR EACH campaign IN LAST 30 DAYS:
IF campaign.acos < 25% AND campaign.orders >= 5 AND DAYS_SINCE_CHANGE("budget") > 7:
campaign.setBudget(MIN($50, campaign.budget * 1.25))
campaign.note("{orders} orders at {acos:percent} — raising budget")
IF campaign.spend > $30 AND campaign.sales = 0 AND DAYS_SINCE_CHANGE("budget") > 7:
campaign.setBudget(MAX($1, campaign.budget * 0.50))
campaign.note("{spend:money} spent, no sales — cutting budget")
END
Harvest converting search terms
Promotes shopper queries that convert into exact keywords in their own ad group. (For cross-campaign harvesting with a visual builder, use Smart Rules.)
FOR EACH searchTerm IN LAST 60 DAYS:
IF searchTerm.orders >= 2 AND searchTerm.acos < 30%:
searchTerm.createKeyword(searchTerm.searchTerm, "exact", MAX($0.10, searchTerm.cpc * 1.10))
searchTerm.note("Harvested: {orders} orders at {acos:percent}")
END
Negate wasteful search terms
Blocks queries that click but never convert. Negatives are precise by default (exact) — see how negation works before switching to phrase.
FOR EACH searchTerm IN LAST 60 DAYS:
IF searchTerm.clicks >= 12 AND searchTerm.orders = 0 AND searchTerm.negated = FALSE:
searchTerm.addNegative(searchTerm.searchTerm, "exact")
searchTerm.note("{clicks} clicks, no orders — negated")
END
Trend brake: this week vs. your baseline
Compares a keyword's recent ACOS to its own longer history and reacts only to genuine deterioration.
FOR EACH keyword IN LAST 7 DAYS:
LET recent_acos = keyword.acos
LET baseline_acos = keyword.acos IN FROM 8 DAYS AGO TO 60 DAYS AGO
IF keyword.clicks >= 10 AND recent_acos > baseline_acos * 1.5:
keyword.setBid(MAX($0.05, keyword.bid * 0.85))
keyword.note("ACOS {recent_acos} vs baseline {baseline_acos}")
END
Placement tuning
Rewards campaigns that convert well with a top-of-search boost.
FOR EACH campaign IN LAST 30 DAYS:
IF campaign.acos < 20% AND campaign.orders >= 10 AND campaign.placement.top_of_search < 50:
campaign.setPlacementBid("TOP OF SEARCH", 50)
campaign.note("Efficient at {acos:percent} — boosting top-of-search")
END
Portfolio housekeeping
Files paused, long-dead campaigns into an archive portfolio so working views stay clean.
FOR EACH campaign IN LIFETIME:
IF campaign.state = "PAUSED" AND campaign.clicks IN LAST 90 DAYS = 0 AND campaign.portfolioName != "Archive":
campaign.moveToPortfolio(PORTFOLIO("Archive"))
campaign.note("Paused and idle 90+ days")
END
Season ramp (pair with a season window)
Save with a season window (e.g. Nov 1 – Dec 20) and a second “ramp-down” rule after it.
# Season: 2026-11-01 → 2026-12-20, schedule: weekly
FOR EACH campaign IN LAST 30 DAYS:
IF campaign.acos < 35% AND campaign.orders >= 3 AND DAYS_SINCE_CHANGE("budget") > 6:
campaign.setBudget(MIN($100, campaign.budget * 1.30))
campaign.note("Q4 ramp: {orders} orders at {acos:percent}")
END
Habits that keep scripts safe. Bound every computed bid/budget with
MAX()/
MIN(); add a
DAYS_SINCE_CHANGE() cool-down to anything that adjusts values; always attach a
note() — future-you reads it in
Change history; and demand enough data (clicks/orders minimums) before letting a rule judge anything.