Skip to content

Negative Keyword Conflict Cleaner

John Williams edited this page Jul 16, 2026 · 1 revision

Negative Keyword Conflict Cleaner — Find Negatives Blocking Your Own Keywords

Script: scripts/cid_negative_conflict_cleaner.js Level: Single account (CID) — see MCC Version for manager accounts What it does: Detects and removes negative keywords that literally block your active (positive) keywords, in one run.

Conflict cleaner overview

What Counts as a Conflict

A conflict exists when a negative keyword's text would block one of your enabled keywords, per Google's literal negative matching rules:

Negative Match Type Blocks a keyword when…
EXACT the keyword text is identical
PHRASE the negative appears as an ordered word sequence inside the keyword
BROAD ALL words of the negative appear anywhere in the keyword, any order

Negatives never use close variants — plurals, misspellings, and apostrophe differences do not match. (That's correct behavior, and it's also why a pure conflict checker misses blocked queries — see the Blocked Search Term Recovery script for that.)

Levels Checked

  1. Account-level negatives — queried from the ACCOUNT_LEVEL_NEGATIVE_KEYWORDS shared set via shared_criterion GAQL, removed via AdsApp.mutate() with a sharedCriterionOperation
  2. Campaign-level negatives — entity iterators, scoped to that campaign's keywords
  3. Ad group-level negatives — scoped to that ad group's keywords
  4. Shared negative keyword lists — scoped to keywords in attached campaigns

Built-In Guardrails

  • DRY_RUN — report-only mode; always run this first
  • MAX_BLOCKED_BEFORE_SKIP (default 25) — a negative blocking 25+ keywords is almost always an intentional brand blocker or traffic sculptor; it's logged as SKIPPED instead of removed
  • Per-level removal toggles — shared lists affect every attached campaign, so that toggle deserves extra thought
  • Complete audit log — every row records level, scope, negative text, match type, sample blocked keywords, and the action taken, so any removal can be reversed manually in seconds

A Word of Caution: Conflicts Aren't Always Mistakes

In well-architected accounts, many "conflicts" are deliberate traffic sculpting — brand negated out of non-brand, exact terms negated out of broad campaigns to route queries, campaign negatives steering traffic between ad groups. Before running with DRY_RUN: false, review the report and ask of each row: is this stale, or is this architecture? The 25-keyword skip threshold catches the most obvious sculptors, but nothing beats knowing your own account structure.

Configuration

var CONFIG = {
  DRY_RUN: true,
  SPREADSHEET_URL: '',
  REMOVE_ACCOUNT_LEVEL: true,
  REMOVE_CAMPAIGN_LEVEL: true,
  REMOVE_ADGROUP_LEVEL: true,
  REMOVE_FROM_SHARED_LISTS: false,
  MAX_BLOCKED_BEFORE_SKIP: 25
};

By AHMEEGO™ / It All Started With A Idea LLC. Discuss in r/ppc_.

Clone this wiki locally