Systems Lab

Agent skill

google-ads-pmax-search-cannibalization-detector

This skill should be used when the user asks to \"detect PMax Search cannibalization\", \"check if PMax is stealing brand traffic\", \"analyze branded ROAS inflation\", \"set up brand exclusions\", or mentions \"PMax cannibalizing Search\", \"incrementality testing\", or \"PMax stealing traffic\".

activeSelf-containedInstructions only1,693 words

Filed under ABM and paid.

From Ad-Superpowers/ad-superpowers-plugin · 120 skills · 5 · pushed 2026-09-10

What it does when it runs

This skill should be used when the user asks to \"detect PMax Search cannibalization\", \"check if PMax is stealing brand traffic\", \"analyze branded ROAS inflation\", \"set up brand exclusions\", or mentions \"PMax cannibalizing Search\", \"incrementality testing\", or \"PMax stealing traffic\". Do NOT use for: general PMax optimization (use performance-max-optimizer), SEO vs SEA keyword gaps (use seo-sea-keyword-gap-analyzer), Shopping structure advice (use shopping-campaign-structure-advisor).

Read from the skill and the 0 files bundled beside it. A skill’s own description is written to be selected by an agent, so it describes the job and not the dependencies.

Keys and connectors you must supply
None found.
Hosts it reaches
No third-party host appears in the skill or its bundled files.
Tool permissions it declares
No allowed-tools in the frontmatter. It only issues instructions, so there is nothing to bound.
Actions present in the files
None. Instructions only.

Ask about google-ads-pmax-search-cannibalization-detector

Opens your assistant with this page's verified links already in the prompt.

Is this safe to install?ClaudeChatGPT
Adapt it to my stackClaudeChatGPT
What else do I need for it to workClaudeChatGPT
Rather ask a human? Talk to Cheetah
git clone --depth 1 --filter=blob:none --sparse https://github.com/Ad-Superpowers/ad-superpowers-plugin.git /tmp/ad-superpowers-plugin
git -C /tmp/ad-superpowers-plugin sparse-checkout set "plugin/skills/google-ads-pmax-search-cannibalization-detector"
mkdir -p ~/.claude/skills/google-ads-pmax-search-cannibalization-detector
cp -R "/tmp/ad-superpowers-plugin/plugin/skills/google-ads-pmax-search-cannibalization-detector/." ~/.claude/skills/google-ads-pmax-search-cannibalization-detector/

Picked up without a restart. A project skill of the same name is shadowed by your personal one. For one repository only, swap ~/.claude/skills for .claude/skills. Claude Code docs ↗

Or take the whole library

This repo ships a .claude-plugin manifest, so Claude Code can install all 120 skills at once. Plugin skills are invoked as /<plugin>:<skill>, so they never collide with your own.

/plugin marketplace add Ad-Superpowers/ad-superpowers-plugin
/plugin

The folder is the same in every client that implements the format — 46 of them — so if yours is not above, only the destination changes.

Reproduced in full from Ad-Superpowers/ad-superpowers-plugin/blob/9b6385d2d2d228e4dac096a1d6bc5715c04fa736/plugin/skills/google-ads-pmax-search-cannibalization-detector/SKILL.md, which is licensed MIT (repository). 1,693 words, 24 headings.

PMax Search Cannibalization Detector

Framework for detecting and resolving Performance Max stealing Search campaign traffic. The #1 frustration in the PPC community -- PMax claims branded conversions making ROAS appear artificially high.

Quick Diagnosis

IS PMAX CANNIBALIZING YOUR SEARCH?
====================================

Step 1: Check PMax search terms report
        -> Many branded terms? --> PROBABLY YES

Step 2: Compare blended vs non-brand ROAS
        -> PMax ROAS 2x higher than Search? --> RED FLAG

Step 3: Check Search campaign metrics since PMax launch
        -> Search impressions/clicks dropped? --> CONFIRMED

RISK LEVELS:
+-- LOW:      <20% brand traffic in PMax search terms
+-- MEDIUM:   20-50% brand traffic -> monitor + consider exclusion
+-- HIGH:     50-70% brand traffic -> enable brand exclusion
+-- CRITICAL: >70% brand traffic -> PMax ROAS is misleading

Step 1: Data Collection

1A: PMax Search Terms Report

Use google_ads_run_gaql to retrieve PMax search terms:

SELECT campaign.name, search_term_view.search_term,
    metrics.impressions, metrics.clicks, metrics.cost_micros,
    metrics.conversions, metrics.conversions_value
FROM search_term_view
WHERE campaign.advertising_channel_type = 'PERFORMANCE_MAX'
AND segments.date DURING LAST_30_DAYS
ORDER BY metrics.impressions DESC
LIMIT 200

1B: Search Campaign Search Terms

SELECT campaign.name, search_term_view.search_term,
    metrics.impressions, metrics.clicks, metrics.cost_micros,
    metrics.conversions, metrics.conversions_value
FROM search_term_view
WHERE campaign.advertising_channel_type = 'SEARCH'
AND segments.date DURING LAST_30_DAYS
ORDER BY metrics.impressions DESC
LIMIT 200

1C: Campaign Performance per Channel Type

SELECT campaign.name, campaign.advertising_channel_type,
    campaign.status, metrics.impressions, metrics.clicks,
    metrics.cost_micros, metrics.conversions,
    metrics.conversions_value, metrics.search_impression_share
FROM campaign
WHERE segments.date DURING LAST_30_DAYS
AND campaign.status = 'ENABLED'
ORDER BY metrics.cost_micros DESC

1D: PMax Channel-Level Breakdown (where the budget goes)

Available from API v22+: segments.ad_network_type works in PMax campaigns, enabling per-channel cost/conversion breakdown.

SELECT campaign.name, segments.ad_network_type,
    metrics.impressions, metrics.clicks, metrics.cost_micros,
    metrics.conversions, metrics.conversions_value
FROM campaign
WHERE campaign.advertising_channel_type = 'PERFORMANCE_MAX'
AND segments.date DURING LAST_30_DAYS
ORDER BY metrics.cost_micros DESC

Network type values: SEARCH, SEARCH_PARTNERS, CONTENT (Display/YouTube), MIXED.

Step 2: Brand Traffic Analysis

Brand vs Non-Brand Classification

BRAND TRAFFIC IDENTIFICATION
============================

Step 1: Define brand terms
+-- Exact brand name + variations
+-- Brand + product combinations
+-- Common typos of brand name
+-- URL-based searches (domain.com)

Step 2: Classify PMax search terms
+-- BRANDED:     Contains brand name or variation
+-- NON-BRANDED: No brand reference
+-- AMBIGUOUS:   Could be either (e.g., product name = brand name)

Step 3: Calculate ratios
+-- Brand Traffic %  = branded impressions / total PMax impressions
+-- Brand Spend %    = branded cost / total PMax cost
+-- Brand Conv %     = branded conversions / total PMax conversions
+-- Brand ROAS       = branded conv value / branded cost

ROAS Inflation Calculation

ROAS INFLATION FRAMEWORK
========================

Blended PMax ROAS = Total conversion value / Total cost
Non-Brand PMax ROAS = Non-brand conversion value / Non-brand cost
Brand-Only ROAS = Brand conversion value / Brand cost

EXAMPLE:
+-- Blended PMax ROAS:   8.5x  <-- This is what Google reports
+-- Brand-Only ROAS:     15.0x <-- Brand traffic always converts well
+-- Non-Brand PMax ROAS: 3.2x  <-- THIS is your real PMax performance
+-- Search Campaign ROAS: 4.1x <-- Compare with this

INFLATION RATIO = Blended ROAS / Non-Brand ROAS
+-- <1.5x -> Minimal inflation
+-- 1.5-2.5x -> Significant inflation
+-- >2.5x -> Severe inflation, PMax claiming mainly brand conversions

Step 3: Decision Tree -- Brand Exclusions

BRAND EXCLUSION DECISION TREE
==============================

Do you have a dedicated Brand Search campaign?
|
+---> NO
|   +---> FIRST: Create Brand Search campaign
|       +-- Exact match brand keywords
|       +-- High budget (brand converts cheaply)
|       +-- Wait 2-4 weeks for data -> go to YES
|
+---> YES -> Analyze PMax brand traffic %
    |
    +---> <20% brand traffic in PMax
    |   +---> NO ACTION NEEDED
    |       +-- PMax is finding mostly new traffic
    |       +-- Monitor monthly
    |
    +---> 20-50% brand traffic in PMax
    |   +---> CONSIDER BRAND EXCLUSION
    |       +-- Compare: Brand Search CPA vs PMax brand CPA
    |       +-- If Search cheaper -> enable exclusion
    |       +-- If PMax cheaper -> monitor, no action
    |
    +---> 50-70% brand traffic in PMax
    |   +---> BRAND EXCLUSION RECOMMENDED
    |       +-- PMax ROAS is misleading
    |       +-- Brand Search campaign is more efficient
    |       +-- Implement exclusion (see action recipes)
    |
    +---> >70% brand traffic in PMax
        +---> BRAND EXCLUSION REQUIRED
            +-- PMax is functioning as expensive Brand campaign
            +-- Non-brand ROAS is probably low
            +-- Also consider: is PMax even valuable?

Step 4: Setting Up Brand Exclusions

Via google_ads_mutate -- Campaign-Level Brand Exclusions

Add negative keywords to PMax campaign (Recipe #7):

operations=[
    {"campaignCriterionOperation": {"create": {
        "campaign": "customers/{CID}/campaigns/{PMAX_CAMPAIGN_ID}",
        "negative": true,
        "keyword": {"text": "[brand name]", "matchType": "PHRASE"}
    }}},
    {"campaignCriterionOperation": {"create": {
        "campaign": "customers/{CID}/campaigns/{PMAX_CAMPAIGN_ID}",
        "negative": true,
        "keyword": {"text": "[brand variation]", "matchType": "PHRASE"}
    }}},
    {"campaignCriterionOperation": {"create": {
        "campaign": "customers/{CID}/campaigns/{PMAX_CAMPAIGN_ID}",
        "negative": true,
        "keyword": {"text": "[brand typo]", "matchType": "BROAD"}
    }}}
]

Via Shared Negative Keyword List (Recipe #9)

Better for multiple PMax campaigns:

operations=[
    {"sharedSetOperation": {"create": {
        "resourceName": "customers/{CID}/sharedSets/-10",
        "name": "Brand Exclusions - PMax", "type": "NEGATIVE_KEYWORDS"
    }}},
    {"sharedCriterionOperation": {"create": {
        "sharedSet": "customers/{CID}/sharedSets/-10",
        "keyword": {"text": "brand name", "matchType": "PHRASE"}
    }}},
    {"sharedCriterionOperation": {"create": {
        "sharedSet": "customers/{CID}/sharedSets/-10",
        "keyword": {"text": "brand.com", "matchType": "BROAD"}
    }}},
    {"campaignSharedSetOperation": {"create": {
        "campaign": "customers/{CID}/campaigns/{PMAX_CAMPAIGN_ID}",
        "sharedSet": "customers/{CID}/sharedSets/-10"
    }}}
]

Step 5: Incrementality Testing Protocol

2-Week On/Off Test

INCREMENTALITY TEST PROTOCOL
==============================

GOAL: Determine if PMax actually delivers incremental conversions
      or only claims existing branded traffic.

WEEK 1-2: BASELINE (PMax ON)
+-- Record: Total account conversions
+-- Record: Search campaign conversions
+-- Record: PMax conversions
+-- Record: Total revenue

WEEK 3-4: TEST (PMax OFF)
+-- Pause PMax campaign
+-- Do NOT increase Search budget (important!)
+-- Measure: Total account conversions
+-- Measure: Search campaign conversions
+-- Measure: Total revenue

ANALYSIS:
+-- Total conversions dropped >20%?
|   +---> PMax delivers incremental value -> turn back on
+-- Total conversions dropped 5-20%?
|   +---> Mixed result -> PMax with brand exclusions
+-- Total conversions same or increased?
|   +---> PMax was only claiming brand traffic
+-- Search conversions increased while total stayed same?
    +---> Confirmed: PMax was cannibalizing Search

Statistical Significance Checklist

SIGNIFICANCE REQUIREMENTS
========================

[ ] Minimum 100 conversions per phase (baseline + test)
[ ] Minimum 14 days per phase (seasonal effects)
[ ] No major external factors (sales, holidays, PR)
[ ] No other campaign changes during test
[ ] Budget kept constant
[ ] No new competitor activity

RELIABILITY:
+-- <100 conversions/phase -> Not reliable, extend test
+-- 100-500 conversions/phase -> Indicative
+-- >500 conversions/phase -> Statistically reliable

Step 6: Monitoring After Exclusions

GAQL Queries for Ongoing Monitoring

After setting brand exclusions, monitor weekly:

-- PMax performance after exclusions (compare with baseline)
SELECT campaign.name, metrics.impressions, metrics.clicks,
    metrics.cost_micros, metrics.conversions,
    metrics.conversions_value
FROM campaign
WHERE campaign.advertising_channel_type = 'PERFORMANCE_MAX'
AND segments.date DURING LAST_7_DAYS
-- Search campaign performance (should increase)
SELECT campaign.name, metrics.impressions, metrics.clicks,
    metrics.cost_micros, metrics.conversions, metrics.conversions_value
FROM campaign
WHERE campaign.advertising_channel_type = 'SEARCH'
AND segments.date DURING LAST_7_DAYS

Expected Results After Brand Exclusion

WHAT TO EXPECT AFTER BRAND EXCLUSION
======================================

PMax Campaign:
+-- Impressions: -30 to -60% (normal, branded volume gone)
+-- Clicks: -20 to -50%
+-- ROAS: DROPS (this is the real non-brand ROAS)
+-- Conversions: -20 to -40%
+-- CPA: INCREASES (non-brand is more expensive, this is normal)

Search Campaigns:
+-- Impressions: +10 to +30% (brand traffic recaptured)
+-- Clicks: +15 to +40%
+-- ROAS: Same or INCREASES
+-- Conversions: +10 to +25%
+-- CPA: Same or DECREASES

Total Account:
+-- Conversions: Same or +5-10% (more efficient budget use)
+-- ROAS: Same or better (fairer distribution)
+-- CPA: Same or better

2026 Updates: PMax Negative Keywords + Search Term Reporting

  • Campaign-level negative keywords are now supported in PMax (API v20+). Use google_ads_mutate Recipe #7 with campaignCriterionOperation — no longer limited to account-level negative lists.
  • Search term reporting for PMax is available (API v21+): query search_term_view with PERFORMANCE_MAX filter (Step 1A above). This was not possible before v21 — if you had old reports showing no data, rerun.
  • url_expansion_opt_out was removed in v22; URL expansion is now controlled via asset group settings.
  • Asset performance labels (Best/Good/Low) were removed from the API in v22 — do not reference them in scripts.

Benchmarks & Red Flags

MetricHealthyWarningCritical
Brand traffic % in PMax<20%20-50%>50%
PMax ROAS / Non-Brand ROAS ratio<1.5x1.5-2.5x>2.5x
Search impressions after PMax launchStable-10 to -25%>-25%
PMax search term relevance>70% relevant50-70%<50%
Brand CPA: PMax vs SearchComparablePMax 1.5x more expensivePMax 2x+ more expensive

Common Patterns

PATTERN 1: "Fantastic PMax ROAS"
+-- Symptom: PMax ROAS 10x+, Search ROAS 3-4x
+-- Cause: PMax claims branded last-click conversions
+-- Diagnosis: Check search terms -> >60% branded
+-- Solution: Brand exclusion -> real ROAS becomes visible

PATTERN 2: "Search performance declining"
+-- Symptom: Search impressions/clicks drop after PMax launch
+-- Cause: PMax wins branded auctions (higher Ad Rank)
+-- Diagnosis: Compare Search metrics before/after PMax
+-- Solution: Brand exclusion on PMax or priority structure

PATTERN 3: "PMax scales budget but no growth"
+-- Symptom: Higher PMax spend, but account ROAS drops
+-- Cause: PMax scales in branded traffic, not new customers
+-- Diagnosis: Calculate non-brand ROAS + incrementality test
+-- Solution: Brand exclusion + budget cap on PMax

PATTERN 4: "Conversion Value Rules not set"
+-- Symptom: All conversions weigh equally
+-- Cause: Brand conversions have different value than non-brand
+-- Diagnosis: Check if CVR differs per traffic source
+-- Solution: Set up value rules (Search Console scope)

Tools Reference

ActionToolRecipe/Query
Get PMax search termsgoogle_ads_run_gaqlsearch_term_view + PERFORMANCE_MAX filter
Search campaign termsgoogle_ads_run_gaqlsearch_term_view + SEARCH filter
Campaign performance per typegoogle_ads_run_gaqlcampaign + advertising_channel_type
Set brand exclusiongoogle_ads_mutateRecipe #7 (negative keywords)
Shared brand exclusion listgoogle_ads_mutateRecipe #9 (shared negative keyword list)
Pause PMax (incrementality test)google_ads_mutateRecipe #2 (pause campaign)
Look up accountsgoogle_ads_list_accounts--

Other skills for the same job

Different authors, same problem. Matched on the words in the skill name, across every library in the catalogue except this one.

Need help setting it up?

This page tells you what google-ads-pmax-search-cannibalization-detector does and what it needs. Cheetah builds the agent setup it runs inside: data, CRM, sequencing and the guardrails.

Book a call →

The directory stays free. There is nothing gated behind this.