Conflicting Negative Keywords and Shared Sets

Negative keywords prevent ads from showing on irrelevant search results. However, an incorrect match type in your negative keywords list can inadvertently block normal keyword matching, decreasing your campaign's effectiveness.

This guide explains how to automate the elimination of conflicting negative keywords using the AdWords API. It also describes the use of shared sets to manage negative keywords and placements.


Assume you own a men's clothing store and want to increase traffic to your site by running a special winter holiday campaign for men's accessories. You recently ran a Search terms report, and noticed the ads in your campaign for men's accessories are also showing up when users search for women's silk scarves.

You have the broad match keywords "silk ties", "wool scarves", and "men's gifts" in your keywords list. You want to add "silk scarves" as a negative keyword, but which negative keyword match type should you choose?

The table below lists some search terms and indicates for each of the three negative match types whether an ad will be blocked.

Search term Negative keyword
-silk scarves
(negative broad)
-"silk scarves"
(negative phrase)
-[silk scarves]
(negative exact)
mens scarves
mens ties
scarves silk Circle-slash
silk ties
silk gift scarves Circle-slash
silk scarves Circle-slash Circle-slash Circle-slash
silk scarves gifts Circle-slash Circle-slash
silk ties wool scarves Circle-slash
womens silk scarves Circle-slash Circle-slash
wool scarves

Note how the broad match negative keyword -silk scarves blocks some valid search terms such as "silk ties wool scarves".

Identifying conflicting negative keywords at scale

It's easy to determine which negative keywords conflict with the positive keywords if you have only a few. It becomes significantly more difficult if you have thousands of keywords and hundreds of negative keywords in your account. Let's examine how to automate this process using the AdWords API.

To facilitate the demonstration, we'll restrict the scenario to positive keywords at the ad group level, and negative keywords at the campaign level.

Retrieving negative keywords

You retrieve the list of campaign-level negative keywords by running a Campaign Negative Keywords Performance report as follows:

def retrieve_negative_keywords(report_utils)
  report_definition = {
    :selector => {
      :fields => ['CampaignId', 'Id', 'KeywordMatchType', 'KeywordText']
    :report_name => 'Negative campaign keywords',
    :download_format => 'CSV',
    :date_range_type => 'TODAY',
    :include_zero_impressions => true

  campaigns = {}

  report = report_utils.download_report(report_definition)
  # Slice off the first row (report name).

  CSV.parse(report, { :headers => true }) do |row|
    campaign_id = row['Campaign ID']

    # Ignore totals row.
    if row[0] != 'Total'
      campaigns[campaign_id] ||=
      negative = Negative.from_csv_row(row)
      campaigns[campaign_id].negatives << negative

  return campaigns

Retrieving positive keywords

Run a Keywords Performance report to retrieve the positive keywords list. The allowed_values parameter allows you to filter out campaigns, ad groups, and keywords by their status, and can accept the values PAUSED and REMOVED.

def retrieve_positive_keyword_report(report_utils, allowed_values)
  report_definition = {
    :selector => {
      :fields => ['CampaignId', 'CampaignName', 'AdGroupId', 'Id',
                  'KeywordMatchType', 'KeywordText'],
      :predicates => [
          :field => 'CampaignStatus',
          :operator => 'IN',
          :values => allowed_values
          :field => 'AdGroupStatus',
          :operator => 'IN',
          :values => allowed_values
          :field => 'Status',
          :operator => 'IN',
          :values => allowed_values
          :field => 'IsNegative',
          :operator => 'IN',
          :values => ['false']
    :report_name => 'Ad group keywords',
    :report_type => 'KEYWORDS_PERFORMANCE_REPORT',
    :download_format => 'CSV',
    :date_range_type => 'TODAY',
    :include_zero_impressions => true

  report = report_utils.download_report(report_definition)
  # Slice off the first row (report name).

  return report

Identifying conflicting negative keywords

Negative keywords work according to the following logic:

  • A broad negative keyword blocks a search term if the search term contains ALL the words in the negative keyword. For example, -silk scarves will block "silk gift scarves" but not "wool scarves".
  • A phrase negative keyword blocks a search term if the search term contains all the words in the negative keyword as a single phrase. For example, -"silk scarves" will block "gift silk scarves" but not "silk gift scarves".
  • An exact negative keyword blocks a search term only if it exactly matches the search term. For example, -[silk scarves] will block "silk scarves" but not "red silk scarves".
  • Keywords are not case sensitive.
  • Close variant matches don't apply to negative keywords. For example, -silk scarves won't block "silk scarf", even though that keyword is a close variant match for "silk scarves".

The following method implements this logic:

def compare_keywords(negatives, positive)
  negatives.each do |negative|
    match_type = negative.match_type.downcase
    negative_text = negative.text.downcase
    positive_text = positive.text.downcase

    match = false

    # Exact matching with negative keywords triggers only when the full text of
    # the keywords is exactly the same.
    # E.g. a negative "silk scarves" will only match "silk scarves", not
    # "red silk scarves".
    if match_type == 'exact'
      match = (negative_text == positive_text)

    # Phrase matching with negative keywords triggers when the negative phrase
    # is present in the target, completely unmodified.
    # E.g. a negative "silk scarves" will match "gift silk scarves", but not
    # "silk gift scarves".
    if match_type == 'phrase'
      negative_tokens = negative_text.split(' ')
      positive_tokens = positive_text.split(' ')

      positive_tokens.each_with_index do |positive_token, positive_index|
        # Iterate until the current token matches the first token in the
        # negative keyword.
        if positive_token == negative_tokens.first
          candidate_match = true
          # Do all of the subsequent tokens also match?
          negative_tokens[1..-1].each_with_index do |token, index|
            if token != positive_tokens[positive_index + index + 1]
              candidate_match = false

          match = candidate_match

    # Broad matching with negative keywords triggers when all of the words are
    # present and exactly the same.
    # E.g. a negative "silk scarves" will match "silk gift scarves", but not
    # "wool scarves".
    if match_type == 'broad'
      negative_tokens = negative_text.split(' ')
      positive_tokens = positive_text.split(' ')

      candidate_match = true

      negative_tokens.each do |token|
        if !positive_tokens.include?(token)
          candidate_match = false

      match = candidate_match

    negative.add_blocked(positive) if match

Deleting conflicting negative keywords

Once you've identified the conflicting negative keywords, you can either:

  • Use the CampaignCriterionService to delete the conflicting negative keywords.
  • Generate a report and delete them manually.

You should periodically repeat this process to ensure that any changes to your lists of keywords or negative keywords have not introduced new conflicts.

Complete example code

The complete code examples used above are part of the AdWords API Ruby client library.

Shared sets

If you have a set of keywords or placements that gives you unwanted impressions or clicks across multiple campaigns, you can create a central list of these negative keywords or placements in AdWords and add it to all of your campaigns. This feature is known as shared sets.

Shared sets help manage your negative keywords and placement exclusions more efficiently by removing the need for duplication of entities across your account.

This section explains how to use the AdWords API to create and work with shared sets.

The code examples use the AdWords API Java library. We have code examples for other supported client libraries as well.

Creating shared sets

To use a shared set of negative keywords, you must first create a shared set, and then populate it with the list of keywords you want to exclude. The following code snippet creates a shared set of negative keywords using the SharedSetService.

// Create the operation.
SharedSetOperation operation = new SharedSetOperation();

// Create the shared set.
SharedSet sharedSet = new SharedSet();
sharedSet.setName("API Negative keyword list for demo");

// Set the type of the shared set. This may be negative keywords or placements.

SharedSetReturnValue retval = sharedSetService.mutate(
    new SharedSetOperation[] {operation});
for (SharedSet set : retval.getValue()) {
  System.out.println("Shared set with id = " + set.getSharedSetId() + ", name = " +
      set.getName() + ", type = " + set.getType() + ", status = " + set.getStatus() +
      "was created.");

Once you've created the shared set, you can use the SharedCriterionService to add keywords to the newly created set. The following code snippet adds two new keywords to the shared set.

String[] keywordTexts = new String[] {"mars cruise", "mars hotels"};

List operations = new ArrayList();
for (String keywordText: keywordTexts) {
  // Create the shared criterion.
  Keyword keyword = new Keyword();

  SharedCriterion sharedCriterion = new SharedCriterion();

  SharedCriterionOperation operation = new SharedCriterionOperation();

SharedCriterionReturnValue retval = sharedCriterionService.mutate(operations.toArray(
    new SharedCriterionOperation[operations.size()]));
for (SharedCriterion sharedCriterion : retval.getValue()) {
  Keyword keyword = (Keyword) sharedCriterion.getCriterion();
  System.out.println("Added keyword with id = " + keyword.getId() + ", text = " +
      keyword.getText() + ", matchtype = " + keyword.getMatchType() + " to shared " +
      "set with id = " + sharedSetId + ".");

You can use the memberCount field to determine the number of keywords or placements in a shared set. Another useful field is referenceCount, which tells you how many campaigns a shared set is associated with.

Applying shared sets to a campaign

Once you've created a shared set, you can attach it to multiple campaigns using CampaignSharedSetService. The following code shows how to attach an existing shared set to a campaign:

// Create the campaign shared set
CampaignSharedSet campaignSharedSet = new CampaignSharedSet();

CampaignSharedSetOperation operation = new CampaignSharedSetOperation();

CampaignSharedSetReturnValue retval = campaignSharedSetService.mutate(
    new CampaignSharedSetOperation[] {operation});
for (CampaignSharedSet attachedCampaignSharedSet : retval.value) {
  System.out.println("Attached shared set with id = " +
      attachedCampaignSharedSet.getSharedSetId() + " to campaign id " +
      attachedCampaignSharedSet.getCampaignId() + ".");

You can use the CampaignSharedSetService.get() method to retrieve the shared sets that have been applied to an existing campaign, as shown below.

// Create the selector.
Selector selector = new Selector();
selector.setFields(new String[] {"SharedSetId", "CampaignId", "SharedSetName",
   "SharedSetType", "Status"});

// Filter your results by specific campaign id.
Predicate predicate = new Predicate();
predicate.setValues(new String[] {campaignId.toString()});

// Filter your results by the type of shared set.
Predicate predicate1 = new Predicate();
predicate1.setValues(new String[] {"NEGATIVE_KEYWORDS", "NEGATIVE_PLACEMENTS"});

selector.setPredicates(new Predicate[] {predicate});

CampaignSharedSetPage page = campaignSharedSetService.get(selector);
if (page.getEntries() != null) {
  for (CampaignSharedSet campaignSharedSet : page.getEntries()) {
    System.out.println("Shared set with id = " + campaignSharedSet.getSharedSetId() +
        ", name = " + campaignSharedSet.getSharedSetName() + ", type = " +
        campaignSharedSet.getSharedSetType() + " and status = " +
        campaignSharedSet.getStatus() + " was found.");


Use the Shared Set report to retrieve reports data for shared sets. Use the Campaign Shared Set report to retrieve reports data for campaign shared sets. The Shared Set Criteria report provides a downloadable snapshot of shared set criteria.

Send feedback about...

AdWords API
AdWords API
Need help? Visit our support page.