/negative-keyword-sweep
Find wasted spend in Google Ads search terms and add negatives at the right scope (account list, campaign, or ad group). Use when the user says "find junk keywords", "stop wasting on bad searches", or wants a search-terms cleanup.
$ npx -y skills add markifact/markifact-mcp --agent claude-codeShips with markifact. Installing the plugin gets this command.
How it fires
How this command gets triggered: by you, by Claude, or both.
- Fires itselfClaude auto-loads it when your prompt matches the work.
- You can call itInvoke it directly when you want it.
- Slash command
/negative-keyword-sweep
Context preview
What this command does when you run it.
Find wasted spend in Google Ads search terms and add negatives at the right scope (account list, campaign, or ad group). Use when the user says "find junk keywords", "stop wasting on bad searches", or wants a search-terms cleanup.
Command definition
negative-keyword-sweep.mddescription: Find wasted spend in Google Ads search terms and add negatives at the right scope (account list, campaign, or ad group). Use when the user says "find junk keywords", "stop wasting on bad searches", or wants a search-terms cleanup.
disable-model-invocation: true
argument-hint: [scope, e.g. "campaign Spring" or "all Search campaigns"]
Goal
Pull the search-terms report, identify spend that isn't producing, group it intelligently, and add negatives at the right level — without nuking legitimate intent.
Inputs to confirm (batch)
1. Account name (substring) — required. 2. Scope — single campaign, list of campaigns, or "all Search campaigns" — required. 3. Lookback — default 30 days; user can override (60 / 90). 4. Waste thresholds — defaults; allow override:
- **No-converter**: spend ≥ $X over lookback AND 0 conversions. Default $X = 2× target CPA, or $50 if no target.
- **Low-CVR offender**: spend ≥ 3× target CPA AND CVR < 25% of account CVR.
- **Off-brand / off-intent**: terms that match a denylist of words (e.g. `free`, `jobs`, `careers`, `tutorial`, `review`, `salary`, `download`, `cheap`, `pirate`, `crack`) — calibrated to vertical.
5. Negative scope preference — default behavior:
- Recurring junk seen across multiple campaigns → **account-level shared negative list**.
- Waste seen only in one campaign → **campaign-level**.
- Single-theme misintent (e.g. "free" only hurts a paid product ad group) → **ad-group-level**.
- Default match type: **phrase**, except for single-word obvious junk → **broad** isn't an option (negatives have no broad), so use **exact** for single tokens like `free`.
Workflow
1. **Discover** ops: `gads_select_accounts`, `gads_list_report_fields`, `gads_get_report`, `gads_create_negative_keyword_list`, `gads_attach_negative_list_to_campaigns`, `gads_add_keywords_to_negative_list`, `gads_add_negative_keywords_to_campaigns`, `gads_add_negative_keywords_to_ad_groups`. 2. **Inspect** every op via `get_operation_inputs`. 3. **Resolve account** (substring). 4. **Pull search-terms report** via `gads_get_report` at the search-term granularity (`search_term_view` resource). Required fields: search_term, campaign_id/name, ad_group_id/name, impressions, clicks, cost, conversions, conv_value, ctr, cvr. 5. **Compute baselines**: account-level CVR over lookback. Compute target CPA if not given (use account history / average across the scope). 6. **Apply thresholds** → candidate list. Tag each candidate with the trigger reason (no-conv, low-CVR, denylist). 7. **Cluster candidates by intent**. Group on shared roots / themes (e.g. `free trial spreadsheet`, `free template excel`, `free download` → cluster "free*"). For each cluster, propose:
- Negative keyword(s) (the minimum tokens needed to block the cluster — usually a phrase).
- Match type (phrase by default; exact for single-token denylist hits; phrase for multi-word patterns).
- Scope (account / campaign / ad-group) per the rules above.
8. **Build a preview table** showing every proposed negative with: keyword, match, scope, target campaigns/ad groups, wasted spend that would have been blocked, and a sample of 1–3 search terms it covers. Wait for user confirmation. 9. **Execute**:
- Account-level batch → if a list with the right purpose already exists, use `gads_add_keywords_to_negative_list`. If not, `gads_create_negative_keyword_list` then `gads_add_keywords_to_negative_list` then `gads_attach_negative_list_to_campaigns`.
- Campaign-level batch → `gads_add_negative_keywords_to_campaigns`.
- Ad-group-level batch → `gads_add_negative_keywords_to_ad_groups`.
10. **Confirm**: count by scope, total estimated spend blocked.
Use `safe-write-operations` for steps 9 onward.
Platform-specific rules the model MUST respect
- **Negatives have no broad match.** Match types are `EXACT`, `PHRASE`, `BROAD` — but Google's docs note that broad-match negatives only block the exact words and their close variants. The mental model: prefer **phrase** for clusters, **exact** for single-word junk.
- **Phrase negative blocks any query containing the phrase in order**, ignoring close variants on individual words. Don't add `running shoes` as exact if you mean to block `cheap running shoes`, `red running shoes`, etc.
- **Don't over-negate.** Negating a single broad term like `software` from a software seller will block legitimate intent. Always look at the cluster's full search-term sample before adding.
- **Account-level shared lists scale better.** Use them for evergreen junk (`jobs`, `careers`, `download`, `crack`). Don't pollute campaigns with the same negative repeated 10 times.
- **Don't add negatives that conflict with active keywords.** If the ad group bids on `free trial`, don't add `free` as a negative — Google will silently stop serving it.
- **PMax has its own negative-keyword surface** (account-level only, requires support request historically; check current op availability). Search negatives don't apply to PMax automatically.
- **Branded terms**: never negate the brand. Always exclude brand variants from candidate clusters.
- **Symmetric vs asymmetric search terms**: if a term has converted at all in lookback (≥ 1 conv), drop it from candidates regardless of CVR thresholds.
Failure modes & recoveries
- Search-terms report empty or near-empty → campaign too new, lookback too short, or campaign isn't Search/PMax. Tell user.
- "Negative keyword conflicts with positive keyword" rejection → list the conflict and ask user to choose.
- Cluster has both winners and losers (some terms convert, some don't) → don't add a phrase negative; recommend tighter ad group structure or single-keyword exact negatives instead.
Output to user
Preview:
| Negative | Match | Scope | Target | Wasted spend blocked | Sample terms | |---|---|---|---|---|---| | free | EXACT | account list "Junk - Free" | all Search | $312 | `free crm`, `free tool` | | jobs | PHRASE | account list
Read more
description: Find wasted spend in Google Ads search terms and add negatives at the right scope (account list, campaign, or ad group). Use when the user says "find junk keywords", "stop wasting on bad searches", or wants a search-terms cleanup. disable-model-invocation: true argument-hint: [scope, e.g. "campaign Spring" or "all Search campaigns"]
Goal
Pull the search-terms report, identify spend that isn't producing, group it intelligently, and add negatives at the right level — without nuking legitimate intent.
Inputs to confirm (batch)
1. Account name (substring) — required. 2. Scope — single campaign, list of campaigns, or "all Search campaigns" — required. 3. Lookback — default 30 days; user can override (60 / 90). 4. Waste thresholds — defaults; allow override:
- **No-converter**: spend ≥ $X over lookback AND 0 conversions. Default $X = 2× target CPA, or $50 if no target.
- **Low-CVR offender**: spend ≥ 3× target CPA AND CVR < 25% of account CVR.
- **Off-brand / off-intent**: terms that match a denylist of words (e.g. `free`, `jobs`, `careers`, `tutorial`, `review`, `salary`, `download`, `cheap`, `pirate`, `crack`) — calibrated to vertical.
5. Negative scope preference — default behavior:
- Recurring junk seen across multiple campaigns → **account-level shared negative list**.
- Waste seen only in one campaign → **campaign-level**.
- Single-theme misintent (e.g. "free" only hurts a paid product ad group) → **ad-group-level**.
- Default match type: **phrase**, except for single-word obvious junk → **broad** isn't an option (negatives have no broad), so use **exact** for single tokens like `free`.
Workflow
1. **Discover** ops: `gads_select_accounts`, `gads_list_report_fields`, `gads_get_report`, `gads_create_negative_keyword_list`, `gads_attach_negative_list_to_campaigns`, `gads_add_keywords_to_negative_list`, `gads_add_negative_keywords_to_campaigns`, `gads_add_negative_keywords_to_ad_groups`. 2. **Inspect** every op via `get_operation_inputs`. 3. **Resolve account** (substring). 4. **Pull search-terms report** via `gads_get_report` at the search-term granularity (`search_term_view` resource). Required fields: search_term, campaign_id/name, ad_group_id/name, impressions, clicks, cost, conversions, conv_value, ctr, cvr. 5. **Compute baselines**: account-level CVR over lookback. Compute target CPA if not given (use account history / average across the scope). 6. **Apply thresholds** → candidate list. Tag each candidate with the trigger reason (no-conv, low-CVR, denylist). 7. **Cluster candidates by intent**. Group on shared roots / themes (e.g. `free trial spreadsheet`, `free template excel`, `free download` → cluster "free*"). For each cluster, propose:
- Negative keyword(s) (the minimum tokens needed to block the cluster — usually a phrase).
- Match type (phrase by default; exact for single-token denylist hits; phrase for multi-word patterns).
- Scope (account / campaign / ad-group) per the rules above.
8. **Build a preview table** showing every proposed negative with: keyword, match, scope, target campaigns/ad groups, wasted spend that would have been blocked, and a sample of 1–3 search terms it covers. Wait for user confirmation. 9. **Execute**:
- Account-level batch → if a list with the right purpose already exists, use `gads_add_keywords_to_negative_list`. If not, `gads_create_negative_keyword_list` then `gads_add_keywords_to_negative_list` then `gads_attach_negative_list_to_campaigns`.
- Campaign-level batch → `gads_add_negative_keywords_to_campaigns`.
- Ad-group-level batch → `gads_add_negative_keywords_to_ad_groups`.
10. **Confirm**: count by scope, total estimated spend blocked.
Use `safe-write-operations` for steps 9 onward.
Platform-specific rules the model MUST respect
- **Negatives have no broad match.** Match types are `EXACT`, `PHRASE`, `BROAD` — but Google's docs note that broad-match negatives only block the exact words and their close variants. The mental model: prefer **phrase** for clusters, **exact** for single-word junk.
- **Phrase negative blocks any query containing the phrase in order**, ignoring close variants on individual words. Don't add `running shoes` as exact if you mean to block `cheap running shoes`, `red running shoes`, etc.
- **Don't over-negate.** Negating a single broad term like `software` from a software seller will block legitimate intent. Always look at the cluster's full search-term sample before adding.
- **Account-level shared lists scale better.** Use them for evergreen junk (`jobs`, `careers`, `download`, `crack`). Don't pollute campaigns with the same negative repeated 10 times.
- **Don't add negatives that conflict with active keywords.** If the ad group bids on `free trial`, don't add `free` as a negative — Google will silently stop serving it.
- **PMax has its own negative-keyword surface** (account-level only, requires support request historically; check current op availability). Search negatives don't apply to PMax automatically.
- **Branded terms**: never negate the brand. Always exclude brand variants from candidate clusters.
- **Symmetric vs asymmetric search terms**: if a term has converted at all in lookback (≥ 1 conv), drop it from candidates regardless of CVR thresholds.
Failure modes & recoveries
- Search-terms report empty or near-empty → campaign too new, lookback too short, or campaign isn't Search/PMax. Tell user.
- "Negative keyword conflicts with positive keyword" rejection → list the conflict and ask user to choose.
- Cluster has both winners and losers (some terms convert, some don't) → don't add a phrase negative; recommend tighter ad group structure or single-keyword exact negatives instead.
Output to user
Preview:
| Negative | Match | Scope | Target | Wasted spend blocked | Sample terms | |---|---|---|---|---|---| | free | EXACT | account list "Junk - Free" | all Search | $312 | `free crm`, `free tool` | | jobs | PHRASE | account list
Showing the first part of this file.
Manage Google Ads, Meta Ads, TikTok Ads, LinkedIn Ads, Microsoft Ads, Reddit Ads, Pinterest Ads, Snapchat Ads, Amazon Ads, DV360, GA4, BigQuery, Google Search Console, Google Business Profile, Google Merchant Center, Facebook, Instagram, LinkedIn, Shopify,
Other commands on markifact.
- /diagnose-underperformer
Diagnose why a specific Google Ads or Meta Ads campaign / ad set / ad is underperforming and prescribe the next concrete action. Use when the user says something like "why is X not converting?", "this campaign is wasting money", or "why are impressions so low?". Read-only — no
Open command - /edit-meta-creative
Edit a Meta ad's creative content — landing page URL, primary text, headline, description, CTA, link description — across one ad or many. Use when the user says "change the URL on my ads", "update the copy", "swap the CTA", or anything that changes what an existing Meta ad
Open command - /launch-google-search
Launch a complete Google Ads Search campaign — campaign, ad group, responsive search ads, keywords, sitelinks, callouts, and negatives — built paused and ready to review. Use when the user wants to spin up a new Search campaign on Google Ads.
Open command - /launch-meta-campaign
Launch a complete Meta Ads campaign — campaign, ad set, and ad(s) — built paused and ready to review. Use when the user wants a new Facebook/Instagram campaign.
Open command - /launch-pmax
Launch a Google Ads Performance Max campaign with a complete asset group, audience signals, and (for ecom) listing groups. Built paused for review. Use when the user wants a new PMax campaign.
Open command - /rotate-creative
Pause fatigued ads and ship fresh creative variants in their place. Works on both Meta and Google Ads. Use when CTR is dropping, frequency is climbing, or the user says "creative is tired" / "let's refresh ads". Always starts new variants paused.
Open command

