Automation

Script cookbook

Ready-to-paste scripts for the most common jobs — copy one, preview it, adjust the thresholds.

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.