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.
Negative placements prevent ads from showing alongside specific types of content or on specific websites, mobile apps, YouTube videos, or YouTube Channels on the Display Network.
This guide explains how to:
- Automate the elimination of conflicting negative keywords using the AdWords API.
- Use shared sets to manage negative keywords and placements.
- Use negative customer criteria to exclude specific placements in all campaigns in your account.
Scenario
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 | |||
silk ties | |||
silk gift scarves | |||
silk scarves | |||
silk scarves gifts | |||
silk ties wool scarves | |||
womens silk scarves | |||
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',
:report_type => 'CAMPAIGN_NEGATIVE_KEYWORDS_PERFORMANCE_REPORT',
: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).
report.slice!(0..report.index("\n"))
CSV.parse(report, { :headers => true }) do |row|
campaign_id = row['Campaign ID']
# Ignore totals row.
if row[0] != 'Total'
campaigns[campaign_id] ||= Campaign.new(campaign_id)
negative = Negative.from_csv_row(row)
campaigns[campaign_id].negatives << negative
end
end
return campaigns
end
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).
report.slice!(0..report.index("\n"))
return report
end
Identifying conflicting negative keywords
Negative keywords work according to the following logic:
- A negative keyword that is stricter than the search term will not block it.
For example,
-[silk scarves]
will not block "red silk scarves". - 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
# If the negative keyword is more strict than the positive one, it cannot
# match.
# E.g. a negative exact "cool shoe" will not prevent positive phrase
# "cool shoe shine".
positive_match_type = positive.match_type.downcase
next if positive_match_type == 'broad' && match_type != 'broad'
next if positive_match_type == 'phrase' && match_type == 'exact'
# 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)
end
# 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
break
end
end
match = candidate_match
end
end
end
# 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
break
end
end
match = candidate_match
end
negative.add_blocked(positive) if match
end
end
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.
Shared set type | Account type | SharedSetService | SharedCriterionService | CampaignSharedSetService |
---|---|---|---|---|
Negative keywords | Client | |||
Negative keywords | Manager | |||
Negative placements | Client | |||
Negative placements | Manager |
This section explains how to use the AdWords API to create and work with shared sets. The example focuses on negative keywords. You can create shared sets of negative placements for client accounts using a similar approach.
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(); operation.setOperator(Operator.ADD); // 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. sharedSet.setType(SharedSetType.NEGATIVE_KEYWORDS); operation.setOperand(sharedSet); 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"}; Listoperations = new ArrayList (); for (String keywordText: keywordTexts) { // Create the shared criterion. Keyword keyword = new Keyword(); keyword.setText(keywordText); keyword.setMatchType(KeywordMatchType.BROAD); SharedCriterion sharedCriterion = new SharedCriterion(); sharedCriterion.setCriterion(keyword); sharedCriterion.setNegative(true); sharedCriterion.setSharedSetId(sharedSetId); SharedCriterionOperation operation = new SharedCriterionOperation(); operation.setOperator(Operator.ADD); operation.setOperand(sharedCriterion); operations.add(operation); } 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(); campaignSharedSet.setCampaignId(campaignId); campaignSharedSet.setSharedSetId(sharedSetId); CampaignSharedSetOperation operation = new CampaignSharedSetOperation(); operation.setOperator(Operator.ADD); operation.setOperand(campaignSharedSet); 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.setField("CampaignId"); predicate.setOperator(PredicateOperator.EQUALS); predicate.setValues(new String[] {campaignId.toString()}); // Filter your results by the type of shared set. Predicate predicate1 = new Predicate(); predicate1.setField("SharedSetType"); predicate1.setOperator(PredicateOperator.IN); 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."); } } }
Reporting
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.
Account-level negative criteria
You can use the CustomerNegativeCriterionService to exclude specific Display Network criteria across all campaigns in your client account.
The service supports the following types of negative criteria:
Adding account-level exclusion criteria is similar to the shared set services, with the following key differences:
Criteria created through
CustomerNegativeCriterionService
apply to all campaigns in your account. With the shared set approach, the criteria will only be excluded in the campaigns associated with the shared set.Excluding a criterion at the account level is a one-step process: simply add the
CustomerNegativeCriterion
. In contrast, excluding a criterion from specific campaigns using shared sets involves multiple steps: create the shared set, add criteria to the shared set, and associate the shared set with one or more campaigns.
The following code snippet shows how to prevent all ads in your account from appearing alongside certain types of content or on a specific website:
Java
// Get the CustomerNegativeCriterionService. CustomerNegativeCriterionServiceInterface customerNegativeCriterionService = adWordsServices.get(session, CustomerNegativeCriterionServiceInterface.class); List<Criterion> criteria = new ArrayList<>(); // Exclude tragedy & conflict content. ContentLabel tragedyContentLabel = new ContentLabel(); tragedyContentLabel.setContentLabelType(ContentLabelType.TRAGEDY); criteria.add(tragedyContentLabel); // Exclude a specific placement. Placement placement = new Placement(); placement.setUrl("http://www.example.com"); criteria.add(placement); // Additional criteria types are available for this service. See the types listed // under Criterion here: // https://developers.google.com/adwords/api/docs/reference/latest/CustomerNegativeCriterionService.Criterion // Create operations to add each of the criteria above. List<CustomerNegativeCriterionOperation> operations = new ArrayList<>(); for (Criterion criterion : criteria) { CustomerNegativeCriterion negativeCriterion = new CustomerNegativeCriterion(); negativeCriterion.setCriterion(criterion); CustomerNegativeCriterionOperation operation = new CustomerNegativeCriterionOperation(); operation.setOperator(Operator.ADD); operation.setOperand(negativeCriterion); operations.add(operation); } // Send the request to add the criteria. CustomerNegativeCriterionReturnValue result = customerNegativeCriterionService.mutate( operations.toArray(new CustomerNegativeCriterionOperation[operations.size()])); // Display the results. for (CustomerNegativeCriterion negativeCriterion : result.getValue()) { System.out.printf( "Customer negative criterion with criterion ID %d and type '%s' was added.%n", negativeCriterion.getCriterion().getId(), negativeCriterion.getCriterion().getCriterionType()); }
C#
using (CustomerNegativeCriterionService customerNegativeCriterionService = (CustomerNegativeCriterionService) user.GetService(AdWordsService.v201809 .CustomerNegativeCriterionService)) { List<Criterion> criteria = new List<Criterion>(); // Exclude tragedy & conflict content. ContentLabel tragedyContentLabel = new ContentLabel { contentLabelType = ContentLabelType.TRAGEDY }; criteria.Add(tragedyContentLabel); // Exclude a specific placement. Placement placement = new Placement { url = "http://www.example.com" }; criteria.Add(placement); // Additional criteria types are available for this service. See the types listed // under Criterion here: // https://developers.google.com/adwords/api/docs/reference/latest/CustomerNegativeCriterionService.Criterion // Create operations to add each of the criteria above. List<CustomerNegativeCriterionOperation> operations = new List<CustomerNegativeCriterionOperation>(); foreach (Criterion criterion in criteria) { CustomerNegativeCriterion negativeCriterion = new CustomerNegativeCriterion { criterion = criterion }; CustomerNegativeCriterionOperation operation = new CustomerNegativeCriterionOperation { @operator = Operator.ADD, operand = negativeCriterion }; operations.Add(operation); } try { // Send the request to add the criteria. CustomerNegativeCriterionReturnValue result = customerNegativeCriterionService.mutate(operations.ToArray()); // Display the results. foreach (CustomerNegativeCriterion negativeCriterion in result.value) { Console.WriteLine( "Customer negative criterion with criterion ID {0} and type '{1}' " + "was added.", negativeCriterion.criterion.id, negativeCriterion.criterion.type); } } catch (Exception e) { throw new System.ApplicationException( "Failed to set customer negative criteria.", e); } }
Python
customer_negative_criterion_service = client.GetService( 'CustomerNegativeCriterionService', version='v201809') criteria = [ # Exclude tragedy & conflict content. { 'xsi_type': 'ContentLabel', 'contentLabelType': 'TRAGEDY' }, # Exclude a specific placement. { 'xsi_type': 'Placement', 'url': 'http://www.example.com' } # Additional criteria types are available for this service. See the types # listed under Criterion here: # https://developers.google.com/adwords/api/docs/reference/latest/CustomerNegativeCriterionService.Criterion ] # Create operations to add each of the criteria above. operations = [{ 'operator': 'ADD', 'operand': { 'criterion': criterion } } for criterion in criteria] # Make the mutate request. result = customer_negative_criterion_service.mutate(operations) # Display the resulting campaign criteria. for negative_criterion in result['value']: print('Customer negative criterion with criterion ID "%s", and type "%s" ' 'was added.' % (negative_criterion['criterion']['id'], negative_criterion['criterion']['type']))
PHP
$customerNegativeCriterionService = $adWordsServices->get( $session, CustomerNegativeCriterionService::class ); $criteria = []; // Exclude tragedy & conflict content. $tragedyContentLabel = new ContentLabel(); $tragedyContentLabel->setContentLabelType(ContentLabelType::TRAGEDY); $criteria[] = $tragedyContentLabel; // Exclude a specific placement. $placement = new Placement(); $placement->setUrl('http://www.example.com'); $criteria[] = $placement; // Additional criteria types are available for this service. See the types // listed under Criterion here: // https://developers.google.com/adwords/api/docs/reference/latest/CustomerNegativeCriterionService.Criterion // Create operations to add each of the criteria above. $operations = []; foreach ($criteria as $criterion) { $negativeCriterion = new CustomerNegativeCriterion(); $negativeCriterion->setCriterion($criterion); $operation = new CustomerNegativeCriterionOperation(); $operation->setOperator(Operator::ADD); $operation->setOperand($negativeCriterion); $operations[] = $operation; } // Add the criteria on the server and print out some information. $result = $customerNegativeCriterionService->mutate($operations); foreach ($result->getValue() as $negativeCriterion) { printf( "Customer negative criterion with criterion ID %d and type '%s' was added.\n", $negativeCriterion->getCriterion()->getId(), $negativeCriterion->getCriterion()->getType() ); }
Perl
# Get the CustomerNegativeCriterionService. my @criteria = (); # Exclude tragedy & conflict content. my $tragedy_content_label = Google::Ads::AdWords::v201809::ContentLabel->new({ contentLabelType => 'TRAGEDY' }); push @criteria, $tragedy_content_label; # Exclude a specific placement. my $placement = Google::Ads::AdWords::v201809::Placement->new({ url => 'http://www.example.com' }); push @criteria, $placement; # Additional criteria types are available for this service. See the types # listed under Criterion here: # https://developers.google.com/adwords/api/docs/reference/latest/CustomerNegativeCriterionService.Criterion # Create operations to add each of the criteria above. my @operations = (); for my $criterion (@criteria) { my $negative_criterion = Google::Ads::AdWords::v201809::CustomerNegativeCriterion->new({ criterion => $criterion }); my $operation = Google::Ads::AdWords::v201809::CustomerNegativeCriterionOperation->new({ operator => 'ADD', operand => $negative_criterion }); push @operations, $operation; } # Send the request to add the criteria. my $result = $client->CustomerNegativeCriterionService() ->mutate({operations => \@operations}); # Display the results. if ($result->get_value()) { foreach my $negative_criterion (@{$result->get_value()}) { printf "Campaign negative criterion with criterion ID %d and type " . "'%s' was added.\n", $negative_criterion->get_criterion()->get_id(), $negative_criterion->get_criterion()->get_type(); } }
Ruby
customer_negative_criterion_srv = adwords.service( :CustomerNegativeCriterionService, API_VERSION) criteria = [] # Exclude tragedy & conflict content. criteria << { :xsi_type => 'ContentLabel', :content_label_type => 'TRAGEDY' } # Exclude a specific placement. criteria << { :xsi_type => 'Placement', :url => 'http://www.example.com' } # Additional criteria types are available for this service. See the types # listed under Criterion here: # https://developers.google.com/adwords/api/docs/reference/latest/CustomerNegativeCriterionService.Criterion # Create operations to add each of the criteria above. operations = criteria.map do |criterion| { :operator => 'ADD', :operand => { :criterion => criterion } } end # Send the request to add the criteria. result = customer_negative_criterion_srv.mutate(operations) # Display the results. result[:value].each do |negative_criterion| puts ("Customer negative criterion with criterion ID %d and type '%s' " + "was added.") % [negative_criterion[:criterion][:id], negative_criterion[:criterion][:criterion_type]] end
VB.NET
Using customerNegativeCriterionService As CustomerNegativeCriterionService = CType(user.GetService(AdWordsService.v201809.CustomerNegativeCriterionService), CustomerNegativeCriterionService) Dim criteria As New List(Of Criterion) ' Exclude tragedy & conflict content. Dim tragedyContentLabel As New ContentLabel() tragedyContentLabel.contentLabelType = ContentLabelType.TRAGEDY criteria.Add(tragedyContentLabel) ' Exclude a specific placement. Dim placement As New Placement() placement.url = "http://www.example.com" criteria.Add(placement) ' Additional criteria types are available for this service. See the types listed ' under Criterion here: ' https://developers.google.com/adwords/api/docs/reference/latest/CustomerNegativeCriterionService.Criterion ' Create operations to add each of the criteria above. Dim operations As New List(Of CustomerNegativeCriterionOperation) For Each criterion As Criterion In criteria Dim negativeCriterion As New CustomerNegativeCriterion() negativeCriterion.criterion = criterion Dim operation As New CustomerNegativeCriterionOperation() operation.operator = [Operator].ADD operation.operand = negativeCriterion operations.Add(operation) Next Try ' Send the request to add the criteria. Dim result As CustomerNegativeCriterionReturnValue = customerNegativeCriterionService.mutate(operations.ToArray()) ' Display the results. For Each negativeCriterion As CustomerNegativeCriterion In result.value Console.WriteLine( "Customer negative criterion with criterion ID {0} and type '{1}' " + "was added.", negativeCriterion.criterion.id, negativeCriterion.criterion.type) Next Catch e As Exception Throw _ New System.ApplicationException("Failed to set customer negative criteria.", e) End Try End Using