paid ads · playbook

How to run a Google Ads campaign review

Parallel workstreams, cross-checks and decision rules for a periodic Google Ads audit.

Updated
Read time
31 min read

How to review a live Google Ads account periodically: split the work into independent parallel workstreams, consolidate and cross-check their findings, then turn them into ranked change proposals and manual tasks.

BigData Boutique defaults: B2B lead gen (consulting/services), long sales cycle, low conversion volume, currency ILS, cost fields in micros (÷ 1,000,000). Thresholds assume lead gen. ROAS variants are noted where they differ.

1. Principles

  1. Measurement first: W1’s verdict gates every CPA/ROAS finding (§7.1).
  2. Business outcomes over platform conversions: judge on cost per SQL/opportunity/pipeline when CRM data exists (W12).
  3. Lag-complete periods only: conversions are reported on the click date, so recent days are under-reported.
  4. Enough data, small steps: act only past §9.1 thresholds; ≤1 spend-affecting change per campaign per cycle.
  5. Every finding cites data (entity, metric, window, number). No generic advice.
  6. Google recommendations are inputs. Optimization score reflects adoption of Google’s recommendations, not performance.
  7. Humans approve every change. Reviews only propose (§8).

2. Cadence & windows

WSWeekly (tactical)Monthly (+)Quarterly (+)
W1 Measurementeach primary action converted in last 7daction inventory, duplicates, goalsenhanced conv., consent, offline import, attribution
W2 Settingschange history, disapprovalsauto-apply, networks, geo, languageaccess, verification, naming, dormant objects
W3 Budget/ISpacing, limited by budgetIS lost budget/rank trendmarginal CPA, reallocation
W4 Biddinglearning/limited statustarget vs actualstrategy fit vs volume, portfolios
W5 Keywords—spend w/o conversions, QSduplicates, zero-impr. cleanup, structure
W6 Search termsyes (highest-ROI weekly task)intent tieringover-negation audit
W7 Ads/assetsdisapprovalsasset performancefull RSA/asset coverage, refresh plan
W8 Landing pagesbroken URLsCVR by pageCWV, message match, forms
W9 Segments—device/geo/hour anomaliesfull segment review incl. demographics
W10 Type fit/PMax/AI Max/DGsearch terms (if live)channel split, asset groupscampaign-type fit, brand exclusions, incrementality
W11 Competition—auction insights trendkeyword-gap scan
W12 Lead quality—lead→SQL by campaignpipeline ROAS, offline value calibration

Windows: weekly = 7d + 28/30d context (label incomplete trailing data); monthly = 30d vs prior 30d; quarterly = 90d vs prior 90d (+ same period last year if seasonal). All lag-complete: if 90% of conversions arrive within L days, the window ends L days ago.

3. Execution model

Phase 4  Follow-up on LAST cycle (runs first)   ─ did applied changes deliver? (§8.5)
Phase 0  Context pack (sequential, small)       ─ goals, targets, windows, lag, entity map
Phase 1  W1…W12 (PARALLEL, independent)         ─ each pulls its own data, returns Findings[]
Phase 2  Consolidation (sequential)             ─ W1 gate, cross-checks, dedupe, root causes
Phase 3  Prioritize & act (sequential)          ─ score, propose changes, create manual tasks, report

Workstreams read only the context pack and their own API data. They never propose; they return candidate_actions. Proposals are filed once, after consolidation, so conflicting or duplicate changes are never filed.

4. Phase 0: Context pack

ItemSource
customer_id, currency, timezonetask fields / customer (A1)
Goal per campaign (leads, SQLs, sales, awareness)goals docs / task
Target CPA/tROAS and max allowable CPA (LTV × close rate)goals / human
Brand terms (incl. products, misspellings); competitor brandshuman / auction insights
Conversion lag profile (days to 90% of conversions)segments.conversion_lag_bucket (A4)
Windows current, previous, context (identical for all workstreams)computed
Entity map: campaign id, name, type, status, bid strategy, budgetcampaign (A5)
Known events: launches, site/tracking changes, holidays, outageschange history + human
Last cycle’s change tasks + their expected_impactPaid Ads board, label google-ads-change (§8.5)
Human policy decisions (e.g. “always bid on brand”)goals / task

5. Phase 1: Workstreams

Severity: 🔴 critical (money burning / data wrong) · 🟠 material · 🟡 hygiene. Thresholds marked [Rn] are defined once in §9.1.

W1 Measurement & conversion integrity

Data: customer (A1), conversion_action (A2), conversion goals, daily conversions per action (A3), lag buckets, upload diagnostics (UI if not in API).

  • Auto-tagging off 🔴 (GCLID needed for GA4 and offline import).
  • Primary actions = real business outcomes. Micro-conversions (page views, scroll, newsletter) primary next to real goals steer bidding to junk 🔴. Make them secondary.
  • Duplicates (GA4 import + Google tag both primary, double-firing thank-you page). Symptom: conversions ≈ 2× CRM leads 🔴.
  • Counting leads = One, purchases = Every 🟠. Values differentiated, not uniform placeholders 🟠. Lookback matches the sales cycle (B2B often 60–90d) and attribution is data-driven 🟡.
  • Recency: a primary action with no conversions in its normal cadence = broken tag 🔴.
  • Volume for bidding per campaign/30d vs [R1] → feeds W4.
  • Enhanced conversions for leads on and healthy 🟠.
  • Offline import (CRM → MQL/SQL/Opp/Won) recent, low error rate. Since June 15, 2026 Google Ads API click-conversion uploads fail for developer tokens with no prior upload history; new integrations must use the Data Manager API. Confirm uploads still land 🔴 if stalled.
  • Consent Mode in place if EEA/UK traffic 🟠.
  • Reconciliation: platform conversions vs CRM leads, gap > [R2] → investigate.

Required verdict: measurement_confidence: high | medium | low + affected actions/campaigns.

W2 Settings, governance & policy

Data: campaign settings (A9), campaign_criterion, change_event (A10), recommendations (A15), ad_group_ad.policy_summary (A11), auto-apply, access and verification (UI).

  • Change history grouped by user and client_type (UI, API, scripts, Google auto-apply). Flag unowned and auto-applied changes. Record dates for X11.
  • Auto-apply recommendations: broad-match upgrades, bid-strategy, keyword-removal or budget auto-apply without a deliberate decision 🟠.
  • Search partners (on by default for Search campaigns): keep only if W9 shows CPA ≤ Google Search 🟠. Display expansion on Search campaigns 🟠.
  • Location option “Presence or interest” on a fixed-market service leaks spend 🟠. Location targets/exclusions and languages match the market 🟡.
  • Tracking template: with URL expansion it must use {lpurl} 🟠. Ad schedule vs ability to respond, ad rotation “Optimize” unless testing 🟡.
  • Policy: disapproved / approved (limited) ads and assets 🔴 on top spenders.
  • Limited ad serving: expanded to more Search scenarios (June 2026) and to all Google Ads (Aug 2026), rolling out gradually through 2028. Check for notices; clear own-brand naming in ads and landing pages is Google’s stated mitigation 🟠.
  • Quarterly: access hygiene (ex-vendors, least privilege, MCC/GA4/YouTube/Merchant links), advertiser verification, dormant objects (paused 90+d, ad groups without ads), naming convention 🟡.

W3 Budget, pacing & impression share

Data: campaign_budget, daily cost, campaign.primary_status(_reasons), IS metrics (A5).

  • Pacing: a campaign can spend up to 2× its average daily budget on a day, but is never charged more than 30.4× per month. Flag projected month spend outside [R3] 🟠.
  • Limited by budget → efficiency decides (X1/X2):
    • lost IS (budget) high + CPA ≤ target → scale candidate ([R4] step);
    • lost IS (budget) high + CPA > target → no budget; fix targeting/queries first;
    • lost IS (rank) high → Ad Rank (quality, bid, assets). Budget won’t help → W5/W7/W11.
  • Brand IS below [R5] 🟠; competitor taking brand traffic 🔴.
  • Spend share vs (qualified) conversion share per campaign / shared budget → reallocation. Quarterly: marginal CPA (weekly spend vs conversions) vs max allowable CPA.
  • Post-Aug 17, 2026: budget-limited tCPA/tROAS campaigns now bid to the target instead of over-performing it (Appendix B). Compare CPA before/after on these campaigns; W4 owns the fix.

W4 Bidding strategy & targets

Data: campaign.bidding_strategy_type, target fields (A5), bidding_strategy (portfolios), campaign.bidding_strategy_system_status, simulators (campaign_simulation, ad_group_criterion_simulation).

  • Strategy fits volume (internal ladder, uses [R1]):

    Conv./campaign/30dFit
    < 15Maximize Conversions (no target), or pool via portfolio / consolidation
    15–30Maximize Conversions + loose tCPA (≈ actual +10–20%)
    30+tCPA, or tROAS if values are real and differentiated
    offline values + 30+ value eventsMaximize Conversion Value / tROAS on pipeline values
  • Maximize Clicks on lead gen with conversion data 🟠.

  • Target realism: anchor to max allowable CPA and actuals. Gap > [R6] → “limited by target” (too low) or overpaying (too high).

  • Budget-limited after Aug 17, 2026: if actuals were well below target, lower the target toward real breakeven ([R4] steps) or drop the target. Google advises waiting 1–2 conversion cycles before judging, and does not adjust targets for you.

  • Status: learning / limited / misconfigured (e.g. no conversion actions in goal) 🔴.

  • Learning resets: repeated large target/budget changes or strategy switches → recommend a freeze.

  • Bid adjustments under Smart Bidding are not used, except device −100%. Leftovers are noise 🟡.

  • Data exclusions must cover tracking-outage dates, or Smart Bidding learns from bad data 🟠.

  • Enhanced CPC was removed for Search/Display (Mar 2025); such campaigns now run as Manual CPC. Migration item 🟡.

W5 Keywords, match types & Quality Score

Data: keyword_view + ad_group_criterion (A7).

  • Spend without conversions ≥ [R7] (lag-complete) → pause / lower bid / tighten match / check LP (X8). Below that, watch.
  • Quality Score (1–10, keyword-level diagnostic, not an auction input). Use impression-weighted QS per campaign. Each component (expected CTR, ad relevance, landing page experience) is rated above / average / below average; a “below average” component points to the fix (copy vs LP vs grouping). Flag QS ≤ 3 with material spend.
  • Match types: spend/conversions by type. Broad only with Smart Bidding and W1 = high. Without offline signals, prefer phrase + exact on core intent.
  • Duplicates / internal competition: one owner per intent. Low CTR on high impressions → W7/W6.
  • Structure: mixed-intent ad groups (30+ keywords) or no-volume single-keyword groups (rule of thumb: 5–20 themed keywords). Zero-impression / “low search volume” keywords 90+d → clean up 🟡.
  • Brand isolation: brand keywords only in brand campaigns; brand terms negative elsewhere.
  • Competitor keywords: own campaign and LP, trademark-policy compliant, higher CPA tolerance.

W6 Search terms & negatives

Data: search_term_view (A6); PMax/AI Max: campaign_search_term_view, segments.search_term_match_source; negatives (A8: campaign_criterion, shared_criterion, negative ad_group_criterion).

  • Tier every term with spend; report % of spend per tier. T3+T4 > [R8] = material leak 🟠/🔴.

    TierMeaningAction
    T1High intent, matches offer (“opensearch consulting”)add as exact/phrase keyword if missing
    T2Relevant, research stage (“opensearch vs elasticsearch performance”)keep if CPA OK; consider own ad group/offer
    T3Tangential / ambiguouswatch; negate at [R7] with 0 conversions
    T4Irrelevant (jobs, free, tutorial, download, login, docs, wrong product)negate now
  • Shared B2B negative list exists: jobs, careers, hiring, salary, resume, internship, course, certification, training, tutorial, “what is”, definition, pdf, examples, free, cheap, download, github, open source (unless the offer), login, documentation, install, error messages, student, “for beginners”.

  • Close-variant drift on exact/phrase. Routing: queries in the wrong ad group/campaign (brand in non-brand, Elasticsearch in OpenSearch) → routing negatives.

  • Positive candidates: converting or T1 terms not yet keywords. Never negate a term that converted (X5). Negatives: exact for one-offs, phrase for bad modifiers, no broad on single generic words.

  • Over-negation (quarterly): negatives blocking T1/T2 or active keywords (X6).

  • PMax / AI Max terms: same tiering. PMax takes up to 10,000 campaign-level negatives.

W7 Ads & assets

Data: ad_group_ad (A11), ad_group_ad_asset_view (A12), campaign_asset / customer_asset.

  • Coverage: every enabled ad group has ≥1 approved, serving RSA (2–3 for testing) 🔴 if none.
  • RSA depth: up to 15 headlines / 4 descriptions with distinct messages (offer, proof, differentiator, CTA, keyword). Ad strength is directional only 🟡.
  • Asset performance: RSA assets have clicks/conversions/cost metrics (data from June 5, 2025); prefer them over performance labels, but treat per-asset ratios (CTR, CPA) as directional. Replace weak assets once they have [R9] volume.
  • Pinning only for legally/brand-critical copy. Message match: top ad groups’ ads state their core intent.
  • B2B qualifiers (“for enterprise teams”, “consulting, not training”) missing where W12 lead quality is poor 🟠.
  • Assets: ≥4 sitelinks with descriptions, real-differentiator callouts, structured snippets, business name + logo; none stale 🟡.
  • Auto-created / AI Max text customization assets: check accuracy and brand voice 🟠. High-spend copy unchanged 6+ months → test a new angle 🟡.

W8 Landing pages & post-click

Data: landing_page_view / expanded_landing_page_view (A13), HTTP fetch of every final URL with spend, PageSpeed/CWV, GA4 if linked.

  • Broken URLs 4xx/5xx 🔴; redirect chains or stripped GCLID 🟠.
  • Weak pages: spend ≥ [R7] with 0 conversions, or CVR < half the account average.
  • Message match keyword → ad → page; homepage for specific intents 🟠. Stale offers/claims 🟡.
  • Core Web Vitals (mobile, 75th percentile): “good” = LCP ≤ 2.5s, INP ≤ 200ms, CLS ≤ 0.1. Failing on top-spend pages 🟠.
  • Form: qualifying fields, works on mobile, conversion fires once (not on reload).
  • URL-expansion destinations (AI Max / PMax): exclude careers, non-converting blog, login, docs, legal 🟠.

W9 Segments: geo, device, schedule, audiences, networks

Data: user_location_view / geographic_view, segments.device, day_of_week, hour, ad_network_type (A14), audience views, user_list.

Compare CPA/CVR per segment vs campaign average; act only at [R10].

  • Geo: spend without conversions; clicks outside the market (→ W2 location option).
  • Device: mobile CPA ≫ desktop → mobile LP fix; under Smart Bidding only device −100% applies.
  • Day/hour: sustained dead zones; ad schedule only if not on Smart Bidding or hours can’t be served.
  • Networks: Search partners / Display CPA materially above Search → exclude (X17).
  • Audiences: observation segments with clearly better/worse CVR; targeting-mode segments that shrink reach; customer and job-seeker exclusions; list sizes and Customer Match upload freshness (flat lists = broken tag/upload) 🟡.
  • Demographics (A17): age, gender, household income, parental status. CPA/CVR and W12 SQL rate per segment vs campaign average; exclude only at [R10].
    • Unknown (*_UNDETERMINED) is targeted by default. Google: exclude it only if you’re sure you want a narrow audience. Never exclude it on CPA alone; report its spend share.
    • Where: Search, Display, Video, Demand Gen support demographic targeting/exclusions (campaign or ad group). PMax: age and gender exclusions only, gender in beta. Household income: only some countries (Israel included).
    • Smart Bidding ignores demographic bid adjustments: only exclusions matter (X14). Partial adjustments only matter on non-Smart strategies (e.g. Manual CPC).
    • B2B (internal rule of thumb): decision-makers skew 25–54, but juniors research and refer. Exclude 18–24 / 65+ only with evidence (R10 + poor SQL rate, X21). Gender/parental/income exclusions need a documented reason.
    • Proposable: campaign-level exclusion via add_campaign_criterion (criterion_type AGE_RANGE/GENDER/PARENTAL_STATUS/INCOME_RANGE, negative: true, demographic_code). Ad-group-level demographic criteria and bid modifiers are manual (manage_ad_group_criterion handles keywords only). The API refuses excluding every value in a dimension.

W10 Campaign-type fit, Performance Max, AI Max, Demand Gen & Display

Campaign-type fit (quarterly): does each goal run on the right type, and is a type missing or redundant? Uncertain direction (PMax vs Search, adding AI Max, Demand Gen test) → experiment (§8.6). Prerequisites and red flags are internal rules of thumb unless a source is cited.

Goal / funnel roleTypePrerequisitesRed flags
Capture own-brand demandSearch, brand-onlybrand list; brand negatives elsewherebrand inside non-brand/PMax (X10); IS < [R5]
Capture high-intent demandSearch non-brand, by serviceW1 high; T1 keywordsmixed intents; Maximize Clicks
Competitor conquestingSearch, own campaignown LP, trademark policy, higher CPA tolerancemixed into non-brand
Expand query reachAI Max for Search (setting on a Search campaign)converting Search campaign, negatives, URL exclusionsjudged < 4 weeks; brand leakage
Conversions across all inventoryPMax[R1]-level volume, qualified/converted-lead goals + offline import (Google lead-gen best practice), assets, brand exclusionsform-fill-only goal → spam (X16)
Create demand / remarketingDemand Gen (YouTube, Discover, Gmail, Maps, GDN)video/image assets, Customer Match/remarketing listsjudged on last-click CPA vs Search
AwarenessVideo (reach)video assets, view-based success metricused for lead CPA
Remarketing / reach (legacy)Display—Display is moving into Demand Gen (Appendix B): plan the migration; no new Display builds
—Shopping, AppN/A for B2B servicesany spend

Proposable: create_campaign makes SEARCH/DISPLAY/SHOPPING/VIDEO/PERFORMANCE_MAX campaigns, always PAUSED. It has no Demand Gen option and no AI Max setting, and it needs an existing explicitly shared budget (budgets are created with the non-allowlisted mutate). A PMax shell is useless without asset groups (also mutate). In practice only a Search campaign on a shared budget is proposable. New budgets, PMax/Demand Gen builds, AI Max toggles and Display→Demand Gen migration are manual (§8.4).

PMax:

  • Channel split (channel performance report / segments.ad_network_type): Display/video absorbing spend at far worse CPA = hidden cross-subsidy 🟠.
  • Brand exclusions when a brand Search campaign exists; otherwise PMax claims brand conversions and inflates results 🔴/🟠.
  • Search terms & negatives: as W6. Search themes: up to 50 per asset group; match T1 intents.
  • Asset groups: themed by persona/product, full creative, audience signals populated (Customer Match, converters, custom segments). Spend/conversions per group (A16).
  • Final URL expansion and its exclusions. Placements: brand-safety scan; account-level placement exclusions.
  • Lead gen: PMax on form fills without offline quality signals often buys spam → X16/W12.

AI Max for Search:

  • Which features are on (search term matching, text customization, final URL expansion), each a deliberate choice. DSA campaigns are being upgraded to AI Max (Appendix B).
  • AI Max-sourced queries (segments.search_term_match_source) vs keyword-matched: CPA, tier mix, lead quality. Brand and URL controls set.
  • Judge after ≥4 weeks / one conversion cycle; review its search terms weekly in month one.

Demand Gen / Display / Video:

  • Judge on funnel role, not last-click CPA vs Search: view-through, assisted conversions, Attributed branded searches (Demand Gen), conversion-lift studies.
  • View-through window and whether view-throughs count in “Conversions”; audience layers per ad group, creative across aspect ratios, frequency caps, placement exclusions.

W11 Competition & market

Data: google_ads_get_auction_insights (IS, overlap, position-above, top-of-page, outranking share per domain), CPC trend, google_ads_generate_keyword_ideas.

  • New/growing competitors (IS or overlap ↑) → explains CPC inflation, lost IS (rank).
  • Position-above ↑ on core campaigns → decide QS (W5) vs bid before raising bids.
  • Competitors in the brand campaign’s auction → brand defense / trademark complaint.
  • Our CPC ↑ with a stable competitor set → our QS or strategy, not the market.
  • Impressions ↓ with stable IS = demand drop; IS ↓ = ours.
  • Quarterly gaps: high-intent ideas with volume and no coverage.
  • Auction-insights metrics can’t be combined with standard metrics in one query; join on campaign + dates.

W12 Business outcomes & lead quality

Data: CRM (HubSpot): leads with GCLID/UTM, lifecycle stage, SQL date, opportunity value, won.

  • Lead → SQL rate by campaign (keyword/term when volume allows); below [R11] = junk source 🟠/🔴.
  • Cost per SQL / opportunity, pipeline ROAS per campaign — use these, not CPA, for budget.
  • Spam/bot leads concentrated in PMax/Display/partners → exclusions, form hardening.
  • Offline values track expected value (MQL ≪ SQL ≪ Opp ≪ Won). Pipeline matures over 30–180d: don’t kill new campaigns before 60–90d unless lead quality is clearly bad.

No CRM data → say so; all efficiency findings become “platform-conversion based”, lower confidence.

6. Finding format

- id: W6-03
  workstream: W6
  title: "Job-seeker queries consume 12% of OpenSearch Consulting spend"
  entities:                       # ids from the context-pack entity map
    campaign_ids: ["2134..."]
    ad_group_ids: []
    criteria: ["opensearch jobs", "opensearch developer salary"]
  evidence:                       # reproducible
    window: "2026-08-01..2026-08-31"
    metrics: {cost_ils: 1840, clicks: 61, conversions: 0, share_of_campaign_spend: 0.12}
    query: "A6"
  severity: material              # critical | material | hygiene
  confidence: high                # high | medium | low (capped by W1 verdict, §7.1)
  est_monthly_impact_ils: 1800    # savings or incremental value, signed
  root_cause_hint: "No shared job/career negative list attached"
  candidate_actions:
    - kind: proposable            # proposable (§8.3 table) | manual (§8.4)
      tool: add_campaign_criterion
      summary: "Add phrase negative 'jobs' to campaign 2134..."
  depends_on: []                  # e.g. ["W1"]
  conflicts_with: []              # filled in Phase 2

No finding without evidence.metrics. Always estimate impact (savings = waste removed; scale = conversions × value). confidence: low when below §9.1 thresholds.

7. Phase 2: Consolidation & cross-checks

7.1 Gate on W1

W1 verdictEffect
highfindings stand
mediumCPA/ROAS-based findings drop one confidence level; volume/hygiene findings unaffected
lowsuspend all bid/budget/target/pause proposals. Report only W1 fixes, conversion-independent waste (T4 terms, broken URLs, disapprovals, settings) and a data exclusion for the broken period

7.2 Cross-check matrix

Each rule merges findings into a root cause, resolves a conflict, or blocks an action.

#If……and…Then
X1W3 limited by budgetCPA > target or poor SQL rateBlock budget ↑. Root cause = efficiency → W5/W6/W8
X2W3 limited by budgetCPA ≤ target, W12 OKScale: budget +[R4], after marginal-CPA check
X3W3 lost IS (rank)W5 low QS / below-avg expected CTRRelevance: copy (W7) / grouping before bids
X4W3 lost IS (rank)W11 competitor position-above ↑Bid/target change only with CPA headroom
X5W6 negative candidateterm/variant converted in context windowDrop the negative
X6W6 new negativean active keyword contains/equals itConflict: rescope to exact or ad-group level
X7W6 positive candidatesame/close keyword exists elsewhereDon’t duplicate; routing negatives or move
X8W5 spend w/o conversionsW8 page broken/slow/poor overallFix LP before pausing keywords
X9high CTR, low CVRW6 queries T1LP/offer problem (W8); if T3/T4 → query problem (W6)
X10W10 PMax strong conversionsbrand IS fell / no brand exclusionsCannibalization: brand exclusions first, re-judge
X11any inflection on date DW2 changes near DAttribute to the change; revert/adjust candidate
X12any bid/budget/target proposalcampaign learning, or spend change < 1 conversion cycle agoDefer (note in report)
X13W4 target changebudget-limited after Aug 17, 2026Target = real breakeven; consider no target
X14W9 segment adjustmentSmart BiddingOnly device −100%, location exclusion, schedule removal apply; drop partial adjustments
X15Google recommendation / auto-applyown finding says oppositeOwn finding wins; dismiss_recommendation
X16strong platform CPApoor SQL rateRank by cost per SQL; feed offline values (W1)
X17W9 partners/Display worseW2 setting onMerge into one settings finding
X18budget increasestheir sumFit the monthly envelope or fund by cuts; else rank and cut
X19several findings on one campaign—≤1 spend-affecting change per campaign per cycle (negatives and assets don’t count)
X20Phase 4: last change didn’t deliver—Revert/adjust before any new proposal on that entity
X21W9 demographic exclusionW12 SQL rate for the segment OK, or segment is UnknownBlock; exclude only when platform CPA and lead quality both fail

7.3 Dedupe, merge, sanity

  • Same entity + action from two workstreams → one finding with both evidences. Many leaves with one cause (20 job queries) → one finding listing the items.
  • Workstream spend totals ≈ account spend for the window (±2%); otherwise someone used the wrong window or filter.
  • Every action references existing, ENABLED ids. Estimated savings ≤ the entity’s spend.
  • Nothing contradicts a recorded human decision.

8. Phase 3: Prioritization, proposals & tasks

8.1 Score and bucket

priority = est_monthly_impact × confidence (high 1.0 / medium 0.6 / low 0.3) ÷ effort (1 = one proposable call, 2 = copy/LP work, 3 = restructure or tracking project). Critical items jump the queue.

  • Fix now: tracking, broken LPs, disapprovals, runaway spend, brand exposure.
  • Quick wins (this cycle, proposable): negatives, pausing clear losers, demographic exclusions, asset swaps, one budget/target step on qualified campaigns. Network/setting fixes are quick but manual (§8.4).
  • Optimizations (2–4 weeks): new RSA angles, restructuring, LP tests, audiences, strategy migrations.
  • Strategic (quarter): offline/value bidding, brand incrementality test, PMax/AI Max adoption or rollback, gap campaigns, cross-channel reallocation.

8.2 How changes are proposed and applied

propose_google_ads_change(tool_name, arguments, customer_id, summary, expected_impact, rationale):

  • One call = one change, highest priority first, capped (weekly ≤10). summary = one-line title; expected_impact (required) quantified from the finding; rationale = evidence + cross-checks passed.
  • Creates a paid-ads/todo task labelled google-ads-change + Google Ads, requires_human=true, assigned to the Ad Change Applier; spend-affecting and destructive changes get high priority.
  • arguments use the tool’s own names and real ids from pulled data. Errors list the expected names — fix or drop. partial_failure/validate_only are refused; an identical open proposal returns duplicate.
  • create_* entities are forced PAUSED; enabling is a separate proposal.
  • A human approves by clearing Requires human; the deterministic applier makes that exact call once and writes an apply ledger. Edited proposals fail their seal — re-propose instead. Review LLMs have no ad write tools.

8.3 Proposable changes (allowlist — anything else is refused)

Findingtool_nameNotes
Campaign negative keyword (or other campaign criterion)add_campaign_criterionexact for one-offs, phrase for modifiers
Demographic exclusion (campaign level)add_campaign_criterionnegative: true + demographic_code; X21
Remove harmful negative / criterionremove_campaign_criterionover-negation audit
Add/pause keyword, change bidmanage_ad_group_criterionspend-affecting
Budget stepupdate_campaign_budget[R4]; spend-affecting
Campaign status, name, start/end date, target CPA / target ROASupdate_campaignonly these fields (plus the EU political-ads flag); one change per proposal; spend-affecting
New RSA / variantcreate_adPAUSED
Pause / edit adupdate_ad
Routing ad group / restructurecreate_ad_group (PAUSED), update_ad_groupupdate_ad_group spend-affecting
Google recommendationapply_recommendation (spend-affecting), dismiss_recommendation
New campaign (keyword gap)create_campaignPAUSED; spend-affecting; needs an existing shared budget (W10)

8.4 Manual tasks (not proposable)

Shared negative lists, conversion actions/goals, campaign settings update_campaign can’t set (networks/Search partners, location option, languages, ad rotation, bid-strategy switches, URL suffix/template), account settings (auto-apply, auto-tagging, brand lists), data exclusions, experiments, Merchant Center, landing-page work, CRM/offline import, policy appeals, access. Create one Paid Ads task per action via the agent’s tasks output (board paid-ads, requires_human: true, source_task_id is stamped automatically) or create_board_task(board_id="paid-ads", ...). Title = the action; description = finding id, evidence pointer and expected impact. Don’t copy the report’s prose. If neither is available, list them as manual in the report’s ranked actions.

8.5 Phase 4: Verify last cycle (run first)

  1. list_board_tasks("paid-ads", "done") (and todo) → google-ads-change tasks since the last review; get_board_task for fields.
  2. Classify: applied (applied_at), failed/held (apply_error, or apply_started_at without applied_at = unknown outcome, needs a human), pending approval (requires_human), rejected (cancelled/archived).
  3. For applied changes, compare expected_impact with a lag-complete before/after window on the entity in applied_resources. Verdict: delivered / partial / no effect / harmful.
  4. Harmful or no effect → revert/adjust candidate (X20). Stale pending proposals → mention in the report, don’t re-propose (duplicates are refused anyway).

8.6 Experiments over guesses

For uncertain-direction changes (strategy switch, broad match/AI Max, PMax vs Search, brand bidding on/off) recommend a Google Ads experiment (50/50, ≥4 weeks or one conversion cycle, success metric agreed upfront, ideally cost per SQL) or a geo holdout — as a manual task, not a direct change.

9. Thresholds & decision rules

9.1 Thresholds (stated once; internal rules of thumb unless marked Google)

IdRuleValue
R1Conversions per campaign/portfolio per 30d for tCPA/tROAS≥ 15 minimum, 30–50 comfortable
R2Platform vs CRM lead gap> 20% → investigate
R3Projected month spend vs 30.4 × daily budget (cap: Google)outside 80–110%
R4Budget/target change per step≤ 20%; one step per conversion cycle
R5Brand search IS≥ 90%
R6Target vs actual CPA/ROAS gap> ±30%
R7Spend with 0 conversions before pausing/negating (keyword, term, LP)≥ 1.5–2× target CPA (or clicks ≥ 2–3 × 1/CVR), lag-complete, ≥ 30 days
R8T3+T4 share of search-term spend> 15–20%
R9Asset volume before replacing~5,000 impressions; “learning” ≥ 2 weeks
R10Segment exclusion (device/geo/hour)segment spend ≥ 2× target CPA and CPA > 2× campaign avg, two consecutive periods
R11Lead → SQL rate (B2B paid)≥ ~20%
—Judge new campaign / strategy / AI Max≥ 4 weeks and ≥ 1 conversion cycle (B2B pipeline 60–90d)
—Core Web Vitals “good” (Google, web.dev)LCP ≤ 2.5s, INP ≤ 200ms, CLS ≤ 0.1

Benchmarks: use the account’s own trailing history; third-party industry averages are not reliable for niche B2B in ILS.

9.2 Pause vs optimize (stop at the first match)

  1. At/under target → keep; scale if lost IS (budget) (X2).
  2. Below R7 data → watch; fix only obvious relevance gaps.
  3. W1 not healthy → don’t pause on CPA; fix tracking.
  4. LP broken/mismatched (W8) → fix LP first.
  5. Queries mostly T3/T4 (W6) → negatives / tighten match; re-evaluate.
  6. QS ≤ 3 with a below-average component → copy/grouping fix; re-evaluate.
  7. Otherwise → lower bid/target; pause if still failing next cycle.

9.3 Other triggers

  • Scale: X2 holds, lost IS (budget) ≥ 10–20%, marginal CPA ≤ max allowable, step R4.
  • Change bid strategy: volume crossed a W4 rung for 2+ months, target chronically unreachable, or offline values arrived. Prefer an experiment.
  • Refresh creative: weak assets at R9, expected CTR below average on top keywords, CTR −20% at stable IS, or copy 6+ months old.
  • Restructure: mixed-intent ad groups, brand + non-brand in one campaign, or fragmentation keeps everything below R1.

10. Symptom → root cause

SymptomCheck firstLikely causes
Conversions dropped suddenlyW1 recency, W2 history, site deploysbroken tag, form/consent change, action edited, LP down, upload stalled
Conversions doubled, CRM flatW1 duplicatesdouble-fire, GA4 import + tag both primary, spam
CPC risingW11, W5 QS trend, W4 targetsnew competitor, quality decay, target loosened, broad expansion
Impressions ↓, IS ↓W3 lost budget/rank, W2 policybudget cut, rank loss, disapprovals, limited ad serving
High CTR, low CVRW6 tiers, W8LP/offer mismatch, info-intent queries, mobile form broken
Good CPA, poor lead qualityW12, W10, W9 partnersspam, network leakage, student queries, no offline signals
CPA drifted up after Aug 17, 2026W3 + W4 budget-limited listbid-to-target change; target above real breakeven
Brand conversions ↓W3 brand IS, W11 brand auction, W10 brand exclusionscompetitor on brand, PMax overlap, brand budget cap

11. Report format

One markdown asset per review, Google Ads Review: <customer_id> <period>. Data, then interpretation, then actions:

  1. Verdict (healthy / needs attention / critical) + W1 measurement confidence.
  2. Scorecard vs previous period and target: spend, conversions, CPA/ROAS, cost per SQL, IS; windows stated.
  3. Last cycle follow-up (§8.5). 4. Critical issues with evidence and action.
  4. Ranked actions: finding id, action, entity, expected impact, confidence, proposable/manual, task id.
  5. Workstream notes, including “checked and fine” (shows coverage).
  6. Deferred / blocked (X1, X12, W1 gate…) with reason and revisit date. 8. Open questions for a human.

Appendix A: GAQL cookbook

Prefer the typed tools (get_campaigns, get_search_terms, …) where one fits. Otherwise run with google_ads_search; the server runs API v23. If a query fails, check the resource with google_ads_describe_gaql_resource instead of guessing. Costs are micros. For lag-complete windows replace DURING LAST_30_DAYS with segments.date BETWEEN 'YYYY-MM-DD' AND 'YYYY-MM-DD'. M is shorthand: expand it to metrics.impressions, metrics.clicks, metrics.cost_micros, metrics.conversions before running.

-- A1 Account (W1/W2)
SELECT customer.id, customer.currency_code, customer.time_zone, customer.auto_tagging_enabled,
  customer.optimization_score, customer.conversion_tracking_setting.conversion_tracking_status FROM customer
-- A2 Conversion actions (W1)
SELECT conversion_action.id, conversion_action.name, conversion_action.type, conversion_action.category,
  conversion_action.primary_for_goal, conversion_action.counting_type,
  conversion_action.click_through_lookback_window_days, conversion_action.attribution_model_settings.attribution_model,
  conversion_action.value_settings.default_value, conversion_action.value_settings.always_use_default_value
FROM conversion_action WHERE conversion_action.status = 'ENABLED'
-- A3 Conversions per action per day (W1)
SELECT segments.date, segments.conversion_action_name, metrics.all_conversions, metrics.conversions
FROM customer WHERE segments.date DURING LAST_30_DAYS
-- A4 Conversion lag (Phase 0)
SELECT segments.conversion_lag_bucket, metrics.conversions FROM campaign WHERE segments.date DURING LAST_90_DAYS
-- A5 Campaigns, IS, status (W3/W4)
SELECT campaign.id, campaign.name, campaign.advertising_channel_type, campaign.bidding_strategy_type,
  campaign.primary_status, campaign.primary_status_reasons, campaign.bidding_strategy_system_status,
  campaign_budget.amount_micros, campaign.target_cpa.target_cpa_micros, campaign.maximize_conversions.target_cpa_micros,
  campaign.target_roas.target_roas, campaign.maximize_conversion_value.target_roas, M, metrics.conversions_value,
  metrics.search_impression_share, metrics.search_budget_lost_impression_share,
  metrics.search_rank_lost_impression_share, metrics.search_top_impression_share,
  metrics.search_absolute_top_impression_share
FROM campaign WHERE campaign.status = 'ENABLED' AND segments.date DURING LAST_30_DAYS
-- A6 Search terms (W6); PMax: FROM campaign_search_term_view (campaign_search_term_view.search_term)
SELECT campaign.id, ad_group.id, search_term_view.search_term, search_term_view.status,
  segments.search_term_match_type, segments.search_term_match_source, M
FROM search_term_view WHERE segments.date DURING LAST_30_DAYS AND metrics.impressions > 0
ORDER BY metrics.cost_micros DESC
-- A7 Keywords + QS (W5). search_predicted_ctr = expected CTR, creative_quality_score = ad relevance,
--    post_click_quality_score = landing page experience
SELECT ad_group.id, ad_group_criterion.criterion_id, ad_group_criterion.keyword.text,
  ad_group_criterion.keyword.match_type, ad_group_criterion.quality_info.quality_score,
  ad_group_criterion.quality_info.search_predicted_ctr, ad_group_criterion.quality_info.creative_quality_score,
  ad_group_criterion.quality_info.post_click_quality_score, M
FROM keyword_view WHERE ad_group_criterion.status = 'ENABLED' AND segments.date DURING LAST_30_DAYS
-- A8 Negatives (W6): campaign level, then shared lists
SELECT campaign.id, campaign_criterion.keyword.text, campaign_criterion.keyword.match_type
FROM campaign_criterion WHERE campaign_criterion.negative = TRUE AND campaign_criterion.type = 'KEYWORD'
SELECT shared_set.id, shared_set.name, shared_criterion.keyword.text, shared_criterion.keyword.match_type
FROM shared_criterion WHERE shared_set.type = 'NEGATIVE_KEYWORDS'
-- A9 Networks, location option, tracking (W2)
SELECT campaign.id, campaign.network_settings.target_search_network,
  campaign.network_settings.target_partner_search_network, campaign.network_settings.target_content_network,
  campaign.geo_target_type_setting.positive_geo_target_type, campaign.final_url_suffix, campaign.tracking_url_template
FROM campaign WHERE campaign.status = 'ENABLED'
-- A10 Change history (W2): date filter within last 30 days and LIMIT (max 10,000) are required
SELECT change_event.change_date_time, change_event.change_resource_type, change_event.resource_change_operation,
  change_event.client_type, change_event.user_email, change_event.changed_fields, campaign.name
FROM change_event WHERE change_event.change_date_time DURING LAST_14_DAYS
ORDER BY change_event.change_date_time DESC LIMIT 1000
-- A11 Ads, strength, policy (W7)
SELECT ad_group.id, ad_group_ad.ad.id, ad_group_ad.status, ad_group_ad.ad.type, ad_group_ad.ad_strength,
  ad_group_ad.policy_summary.approval_status, ad_group_ad.policy_summary.review_status, ad_group_ad.ad.final_urls, M
FROM ad_group_ad WHERE ad_group_ad.status != 'REMOVED' AND segments.date DURING LAST_30_DAYS
-- A12 RSA assets (W7)
SELECT ad_group_ad.ad.id, asset.text_asset.text, ad_group_ad_asset_view.field_type,
  ad_group_ad_asset_view.performance_label, ad_group_ad_asset_view.pinned_field, M
FROM ad_group_ad_asset_view WHERE segments.date DURING LAST_30_DAYS
-- A13 Landing pages (W8)
SELECT landing_page_view.unexpanded_final_url, M, metrics.mobile_friendly_clicks_percentage, metrics.speed_score
FROM landing_page_view WHERE segments.date DURING LAST_30_DAYS
-- A14 Segments (W9): swap the segments for segments.day_of_week, segments.hour
SELECT campaign.id, segments.ad_network_type, segments.device, M FROM campaign WHERE segments.date DURING LAST_30_DAYS
SELECT campaign.id, user_location_view.country_criterion_id, user_location_view.targeting_location, M
FROM user_location_view WHERE segments.date DURING LAST_30_DAYS
-- A15 Recommendations (W2)
SELECT recommendation.resource_name, recommendation.type, recommendation.campaign, recommendation.dismissed,
  recommendation.impact.base_metrics.conversions, recommendation.impact.potential_metrics.conversions
FROM recommendation
-- A16 PMax asset groups (W10)
SELECT campaign.id, asset_group.id, asset_group.name, asset_group.status, asset_group.ad_strength,
  metrics.cost_micros, metrics.conversions, metrics.conversions_value
FROM asset_group WHERE segments.date DURING LAST_30_DAYS
-- A17 Demographics (W9): swap to gender_view/gender.type, income_range_view/income_range.type,
--     parental_status_view/parental_status.type. Ad-group based: PMax is not covered
SELECT campaign.id, ad_group.id, ad_group_criterion.age_range.type, ad_group_criterion.negative, M
FROM age_range_view WHERE segments.date DURING LAST_30_DAYS

Auction insights (W11): google_ads_get_auction_insights per campaign and date range.

Appendix B: Platform changes that affect audits

WhenChangeAudit implication
Mar 2025Enhanced CPC removed for Search/Display (Shopping since Oct 2023); runs as Manual CPCmigrate strategy (W4)
Jun 2025 →RSA asset-level clicks, conversions, cost (data from June 5, 2025)prefer over performance labels (W7)
2025–2026PMax: search terms report, channel performance, up to 10,000 campaign negatives, 50 search themes per asset group, brand exclusionstreat PMax like Search in W6; check cross-subsidy and cannibalization (W10)
2025–2026AI Max for Search (search term matching, text customization, final URL expansion)audit each feature, URL exclusions, {lpurl} templates, AI copy, query quality (W10)
Jan 2026Attributed branded searches metric for Demand Genupper-funnel value beyond last click (W10)
Jun 2026Limited ad serving expanded on Search; Aug 2026 to all Google Ads; gradual through 2028check notices, own-brand clarity (W2)
Jun 15, 2026Google Ads API click-conversion uploads fail for tokens with no prior upload history; Data Manager API is the path for new integrationsverify offline imports land (W1, W12)
Aug 17–27, 2026Budget-limited tCPA/tROAS (Search, Shopping, PMax, Demand Gen, Travel) now bid to target instead of beating itre-baseline targets; wait 1–2 conversion cycles; before/after (W3/W4)
Jun 2026 →Display campaigns move to Demand Gen (GDN on Demand Gen): migration tool from Jun 2026; later, new Display campaigns only in Demand Gen and remaining ones auto-migrated (no dates yet); no revertplan migration as a manual task (W10)
Sep 2026 → Feb 2027Eligible Search campaigns with legacy settings auto-upgrade to AI Max from Sep 2026; DSA upgrade extended to Feb 2027check which AI Max features were switched on (W10)