negative-keyword-sweep
SkillSearchFind 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.
Available today. Use it from your connected AI after setup.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the negative-keyword-sweep skill
What this skill tells your AI
The instructions your AI receives, as published by markifact/markifact-mcp in plugins/hermes/markifact/skills/negative-keyword-sweep/SKILL.md and read by ahel’s review.
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)
- Account name (substring) — required.
- Scope — single campaign, list of campaigns, or "all Search campaigns" — required.
- Lookback — default 30 days; user can override (60 / 90).
- 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.
- 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
- 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. - Inspect every op via
get_operation_inputs. - Resolve account (substring).
- Pull search-terms report via
gads_get_reportat the search-term granularity (search_term_viewresource). Required fields: search_term, campaign_id/name, ad_group_id/name, impressions, clicks, cost, conversions, conv_value, ctr, cvr. - Compute baselines: account-level CVR over lookback. Compute target CPA if not given (use account history / average across the scope).
- Apply thresholds → candidate list. Tag each candidate with the trigger reason (no-conv, low-CVR, denylist).
- 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.
- 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.
- 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_listthengads_add_keywords_to_negative_listthengads_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.
- Account-level batch → if a list with the right purpose already exists, use
- 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 shoesas exact if you mean to blockcheap running shoes,red running shoes, etc. - Don't over-negate. Negating a single broad term like
softwarefrom 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 addfreeas 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 "Junk - Jobs" | all Search | $147 | crm jobs, software jobs |
| ... |
After execute:
| Scope | Negatives added | Spend that would have been blocked (last 30d) |
|---|---|---|
| Account list | $ | |
| Campaign | $ | |
| Ad group | $ |
End with: "Re-check search terms in 30 days — patterns shift as keywords mature."
Signals
- GitHub stars
- 48
- Forks
- 8
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Key
negative-keyword-sweep- Source
- github.com/markifact/markifact-mcp