The Keyword Planner in the Google Ads UI proposes new keyword ideas based on your existing keywords and website. You can then retrieve historical statistics for keywords (Average CPC, Monthly Search Volume, etc.) to help you decide whether to use these keyword ideas or not.
You can achieve similar goals in the AdWords API with the TargetingIdeaService, that allows you to retrieve targeting ideas from your own tool/platform to help automate account optimization.
Use case
TargetingIdeaService can be used to get new keyword ideas based on a number of inputs, such as:
- Seed Keywords
- Seed Ad Group ID
- Target Website
- Location
- Language
- Product and Services Category
- And many more
For example, if you're creating a new campaign, or optimizing an existing campaign, you can use the TargetingIdeaService to retrieve a list of candidate keywords based on your existing taxonomy of keywords.
You can use the service to obtain results similar to the functions under Find new keywords and get search volume data in Keyword Planner:
The Keyword Planner function Plan your budget and get forecasts is covered in Estimating Traffic.
Retrieving keyword ideas
The primary input to the TargetingIdeaService is the
TargetingIdeaSelector
.
The following is an example of
getting keyword ideas from the TargetingIdeaService by using a couple of seed
keywords.
Prepare the request
First, prepare the TargetingIdeaSelector
with parameters that indicate the
request's purpose is to retrieve keyword ideas:
Java
TargetingIdeaSelector selector = new TargetingIdeaSelector(); selector.setRequestType(RequestType.IDEAS); selector.setIdeaType(IdeaType.KEYWORD);
C#
TargetingIdeaSelector selector = new TargetingIdeaSelector { requestType = RequestType.IDEAS, ideaType = IdeaType.KEYWORD,
Python
selector = { 'ideaType': 'KEYWORD', 'requestType': 'IDEAS' }
PHP
$selector = new TargetingIdeaSelector(); $selector->setRequestType(RequestType::IDEAS); $selector->setIdeaType(IdeaType::KEYWORD);
Perl
my $selector = Google::Ads::AdWords::v201809::TargetingIdeaSelector->new({ requestType => "IDEAS", ideaType => "KEYWORD" });
Ruby
selector = { :idea_type => 'KEYWORD', :request_type => 'IDEAS' }
VB.NET
Dim selector As New TargetingIdeaSelector() selector.requestType = RequestType.IDEAS selector.ideaType = IdeaType.KEYWORD
Next, select the attributes that must be retrieved. Attributes can be thought of as individual columns/fields that are related to the keyword:
Java
selector.setRequestedAttributeTypes(new AttributeType[] { AttributeType.KEYWORD_TEXT, AttributeType.SEARCH_VOLUME, AttributeType.AVERAGE_CPC, AttributeType.COMPETITION, AttributeType.CATEGORY_PRODUCTS_AND_SERVICES});
C#
requestedAttributeTypes = new AttributeType[] { AttributeType.KEYWORD_TEXT, AttributeType.SEARCH_VOLUME, AttributeType.AVERAGE_CPC, AttributeType.COMPETITION, AttributeType.CATEGORY_PRODUCTS_AND_SERVICES } };
Python
selector['requestedAttributeTypes'] = [ 'KEYWORD_TEXT', 'SEARCH_VOLUME', 'CATEGORY_PRODUCTS_AND_SERVICES']
PHP
$selector->setRequestedAttributeTypes( [ AttributeType::KEYWORD_TEXT, AttributeType::SEARCH_VOLUME, AttributeType::AVERAGE_CPC, AttributeType::COMPETITION, AttributeType::CATEGORY_PRODUCTS_AND_SERVICES ] );
Perl
$selector->set_requestedAttributeTypes([ "KEYWORD_TEXT", "SEARCH_VOLUME", "AVERAGE_CPC", "COMPETITION", "CATEGORY_PRODUCTS_AND_SERVICES" ]);
Ruby
selector[:requested_attribute_types] = [ 'KEYWORD_TEXT', 'SEARCH_VOLUME', 'AVERAGE_CPC', 'COMPETITION', 'CATEGORY_PRODUCTS_AND_SERVICES', ]
VB.NET
selector.requestedAttributeTypes = New AttributeType() { _ AttributeType.KEYWORD_TEXT, AttributeType.SEARCH_VOLUME, AttributeType.CATEGORY_PRODUCTS_AND_SERVICES }
Next, configure the selector's Paging
to limit the number of results returned by a single request. Paging
is a
required field of
TargetingIdeaSelector
.
Java
// Set selector paging (required for targeting idea service). Paging paging = new Paging(); paging.setStartIndex(0); paging.setNumberResults(10); selector.setPaging(paging);
C#
// Set selector paging (required for targeting idea service). selector.paging = Paging.Default;
Python
offset = 0 selector['paging'] = { 'startIndex': str(offset), 'numberResults': str(PAGE_SIZE) }
PHP
$paging = new Paging(); $paging->setStartIndex(0); $paging->setNumberResults(10); $selector->setPaging($paging);
Perl
# Set selector paging (required for targeting idea service). my $paging = Google::Ads::AdWords::v201809::Paging->new({ startIndex => 0, numberResults => 10 }); $selector->set_paging($paging);
Ruby
selector[:paging] = { :start_index => 0, :number_results => PAGE_SIZE }
VB.NET
' Set selector paging (required for targeting idea service). selector.paging = Paging.Default
Finally, use the
RelatedToQuerySearchParameter
to specify a list of seed keywords from which to generate new ideas:
Java
List<SearchParameter> searchParameters = new ArrayList<>(); // Create related to query search parameter. RelatedToQuerySearchParameter relatedToQuerySearchParameter = new RelatedToQuerySearchParameter(); relatedToQuerySearchParameter.setQueries(new String[] {"bakery", "pastries", "birthday cake"}); searchParameters.add(relatedToQuerySearchParameter);
C#
// Create related to query search parameter. RelatedToQuerySearchParameter relatedToQuerySearchParameter = new RelatedToQuerySearchParameter { queries = new string[] { "bakery", "pastries", "birthday cake" } }; searchParameters.Add(relatedToQuerySearchParameter);
Python
selector['searchParameters'] = [{ 'xsi_type': 'RelatedToQuerySearchParameter', 'queries': ['space cruise'] }]
PHP
$searchParameters = []; // Create related to query search parameter. $relatedToQuerySearchParameter = new RelatedToQuerySearchParameter(); $relatedToQuerySearchParameter->setQueries( [ 'bakery', 'pastries', 'birthday cake' ] ); $searchParameters[] = $relatedToQuerySearchParameter;
Perl
# Create related to query search parameter. my @search_parameters = (); my $related_to_query_search_parameter = Google::Ads::AdWords::v201809::RelatedToQuerySearchParameter->new( {queries => ["bakery", "pastries", "birthday cake"]}); push @search_parameters, $related_to_query_search_parameter;
Ruby
search_parameters = [] search_parameters << { # The 'xsi_type' field allows you to specify the xsi:type of the object # being created. It's only necessary when you must provide an explicit # type that the client library can't infer. :xsi_type => 'RelatedToQuerySearchParameter', :queries => [keyword_text] }
VB.NET
' Create related to query search parameter. Dim relatedToQuerySearchParameter As New RelatedToQuerySearchParameter() relatedToQuerySearchParameter.queries = New String() { _ "bakery", "pastries", "birthday cake" } searchParameters.Add(relatedToQuerySearchParameter)
Once you've configured the TargetingIdeaSelector
, send it through the get
operation to retrieve keyword ideas:
Java
// Get keyword ideas. TargetingIdeaPage page = targetingIdeaService.get(selector);
C#
// Get related keywords. page = targetingIdeaService.get(selector);
Python
page = targeting_idea_service.get(selector)
PHP
// Get keyword ideas. $page = $targetingIdeaService->get($selector);
Perl
# Get keyword ideas. my $page = $client->TargetingIdeaService()->get({selector => $selector});
Ruby
begin # Perform request. If this loop executes too many times in quick suggestion, # you may get a RateExceededError. See here for more info on handling these: # https://developers.google.com/adwords/api/docs/guides/rate-limits page = targeting_idea_srv.get(selector) results += page[:entries] if page and page[:entries] # Prepare next page request. offset += PAGE_SIZE selector[:paging][:start_index] = offset end while offset < page[:total_num_entries]
VB.NET
' Get related keywords. page = targetingIdeaService.get(selector)
Process the response
The response is composed of a list of
TargetingIdea
objects. Each TargetingIdea
object contains a map of
AttributeType
associated with an
Attribute
.
It's easier to think of each TargetingIdea
object as a data row, each
AttributeType
as a field/column of the row, and the corresponding Attribute
as the value for that data cell.
Java
// Display keyword ideas. for (TargetingIdea targetingIdea : page.getEntries()) { Map<AttributeType, Attribute> data = Maps.toMap(targetingIdea.getData()); StringAttribute keyword = (StringAttribute) data.get(AttributeType.KEYWORD_TEXT); IntegerSetAttribute categories = (IntegerSetAttribute) data.get(AttributeType.CATEGORY_PRODUCTS_AND_SERVICES); String categoriesString = "(none)"; if (categories != null && categories.getValue() != null) { categoriesString = Joiner.on(", ").join(Ints.asList(categories.getValue())); } Long averageMonthlySearches = ((LongAttribute) data.get(AttributeType.SEARCH_VOLUME)) .getValue(); Money averageCpc = ((MoneyAttribute) data.get(AttributeType.AVERAGE_CPC)).getValue(); Double competition = ((DoubleAttribute) data.get(AttributeType.COMPETITION)).getValue(); System.out.printf("Keyword with text '%s', average monthly search volume %d, " + "average CPC %d, and competition %.2f " + "was found with categories: %s%n", keyword.getValue(), averageMonthlySearches, averageCpc.getMicroAmount(), competition, categoriesString); }
C#
// Display related keywords. if (page.entries != null && page.entries.Length > 0) { foreach (TargetingIdea targetingIdea in page.entries) { Dictionary<AttributeType, Google.Api.Ads.AdWords.v201809.Attribute> ideas = targetingIdea.data.ToDict(); string keyword = (ideas[AttributeType.KEYWORD_TEXT] as StringAttribute).value; IntegerSetAttribute categorySet = ideas[AttributeType.CATEGORY_PRODUCTS_AND_SERVICES] as IntegerSetAttribute; string categories = ""; if (categorySet != null && categorySet.value != null) { categories = string.Join(", ", categorySet.value); } long averageMonthlySearches = (ideas[AttributeType.SEARCH_VOLUME] as LongAttribute).value; Money averageCpc = (ideas[AttributeType.AVERAGE_CPC] as MoneyAttribute).value; double competition = (ideas[AttributeType.COMPETITION] as DoubleAttribute).value; Console.WriteLine( "Keyword with text '{0}', average monthly search volume {1}, " + "average CPC {2}, and competition {3:F2} was found with " + "categories: {4}", keyword, averageMonthlySearches, averageCpc?.microAmount, competition, categories); Console.WriteLine( "Keyword with text '{0}', and average monthly search volume " + "'{1}' was found with categories: {2}", keyword, averageMonthlySearches, categories); i++; } }
Python
for result in page['entries']: attributes = {} for attribute in result['data']: attributes[attribute['key']] = getattr( attribute['value'], 'value', '0') print('Keyword with "%s" text and average monthly search volume ' '"%s" was found with Products and Services categories: %s.' % (attributes['KEYWORD_TEXT'], attributes['SEARCH_VOLUME'], attributes['CATEGORY_PRODUCTS_AND_SERVICES']))
PHP
// Print out some information for each targeting idea. $entries = $page->getEntries(); if ($entries !== null) { foreach ($entries as $targetingIdea) { $data = MapEntries::toAssociativeArray($targetingIdea->getData()); $keyword = $data[AttributeType::KEYWORD_TEXT]->getValue(); $searchVolume = ($data[AttributeType::SEARCH_VOLUME]->getValue() !== null) ? $data[AttributeType::SEARCH_VOLUME]->getValue() : 0; $averageCpc = $data[AttributeType::AVERAGE_CPC]->getValue(); $competition = $data[AttributeType::COMPETITION]->getValue(); $categoryIds = ($data[AttributeType::CATEGORY_PRODUCTS_AND_SERVICES]->getValue() === null) ? $categoryIds = '' : implode( ', ', $data[AttributeType::CATEGORY_PRODUCTS_AND_SERVICES]->getValue() ); printf( "Keyword with text '%s', average monthly search volume %d, " . "average CPC %d, and competition %.2f was found with categories: %s\n", $keyword, $searchVolume, ($averageCpc === null) ? 0 : $averageCpc->getMicroAmount(), $competition, $categoryIds ); } }
Perl
foreach my $targeting_idea (@{$page->get_entries()}) { my $data = Google::Ads::Common::MapUtils::get_map($targeting_idea->get_data()); my $keyword = $data->{"KEYWORD_TEXT"}->get_value(); my $search_volume = $data->{"SEARCH_VOLUME"}->get_value() ? $data->{"SEARCH_VOLUME"}->get_value() : 0; my $categories = $data->{"CATEGORY_PRODUCTS_AND_SERVICES"}->get_value() ? $data->{"CATEGORY_PRODUCTS_AND_SERVICES"}->get_value() : []; my $average_cpc = $data->{"AVERAGE_CPC"}->get_value()->get_microAmount(); my $competition = $data->{"COMPETITION"}->get_value(); printf "Keyword with text '%s', monthly search volume %d, average CPC" . " %d, and competition %.2f was found with categories: '%s'\n", $keyword, $search_volume, $average_cpc, $competition, join(", ", @{$categories}); }
Ruby
# Display results. results.each do |result| data = result[:data] keyword = data['KEYWORD_TEXT'][:value] average_cpc = data['AVERAGE_CPC'][:value] competition = data['COMPETITION'][:value] products_and_services = data['CATEGORY_PRODUCTS_AND_SERVICES'][:value] average_monthly_searches = data['SEARCH_VOLUME'][:value] puts ("Keyword with text '%s', average monthly search volume %d, " + "average CPC %d, and competition %.2f was found with categories: %s") % [ keyword, average_monthly_searches, average_cpc[:micro_amount], competition, products_and_services ] end
VB.NET
'Display the results. If Not page.entries Is Nothing AndAlso page.entries.Length > 0 Then For Each targetingIdea As TargetingIdea In page.entries For Each entry As Type_AttributeMapEntry In targetingIdea.data ' Preferred: Use targetingIdea.data.ToDict() if you are not on ' Mono. Dim ideas As Dictionary(Of AttributeType, Attribute) = MapEntryExtensions.ToDict (Of AttributeType, Attribute)( targetingIdea.data) Dim keyword As String = DirectCast(ideas(AttributeType.KEYWORD_TEXT), StringAttribute).value Dim categorySet As IntegerSetAttribute = DirectCast( ideas(AttributeType.CATEGORY_PRODUCTS_AND_SERVICES), IntegerSetAttribute) Dim categories As String = "" If _ (Not categorySet Is Nothing) AndAlso (Not categorySet.value Is Nothing) Then categories = String.Join(", ", categorySet.value) End If Dim averageMonthlySearches As Long = DirectCast(ideas(AttributeType.SEARCH_VOLUME), LongAttribute).value Dim averageCpcMoney As Money = DirectCast(ideas(AttributeType.AVERAGE_CPC), MoneyAttribute).value Dim averageCpc As Long If (Not averageCpcMoney Is Nothing) Then averageCpc = averageCpcMoney.microAmount End If Dim competition As Double = DirectCast(ideas(AttributeType.COMPETITION), DoubleAttribute).value Console.WriteLine( "Keyword with text '{0}', average monthly search " + "volume {1}, average CPC {2}, and competition {3:F2} was " & "found with categories: {4}", keyword, averageMonthlySearches, averageCpc, competition, categories) Next i = i + 1 Next End If selector.paging.IncreaseOffset() Loop While (selector.paging.startIndex < page.totalNumEntries) Console.WriteLine("Number of related keywords found: {0}", page.totalNumEntries) Catch e As Exception Throw New System.ApplicationException("Failed to retrieve related keywords.", e) End Try End Using End Sub End Class End Namespace
The example request selected four attributes, so a sample response may look like this:
KEYWORD_TEXT | SEARCH_VOLUME | AVERAGE_CPC | COMPETITION |
---|---|---|---|
cake bakery | 170 | 0.485384 | 0.41 |
bakery story | 390 | 0.069918 | 0.12 |
... | ... | ... | ... |
Retrieving keyword stats
Retrieving historical keyword statistics is very similar to retrieving keyword
ideas. The only difference is that the RequestType
must be set to STATS
instead.
Seeding with an ad group ID
If you already have an existing campaign and ad group, you can retrieve
targeting ideas by providing the ad group ID with the
SeedAdGroupIdSearchParameter
.
Java
SeedAdGroupIdSearchParameter seedAdGroupIdSearchParameter = new SeedAdGroupIdSearchParameter(); seedAdGroupIdSearchParameter.setAdGroupId(adGroupId); searchParameters.add(seedAdGroupIdSearchParameter);
C#
SeedAdGroupIdSearchParameter seedAdGroupIdSearchParameter = new SeedAdGroupIdSearchParameter { adGroupId = adGroupId.Value }; searchParameters.Add(seedAdGroupIdSearchParameter);
Python
# Use an existing ad group to generate ideas (optional) if ad_group_id is not None: selector['searchParameters'].append({ 'xsi_type': 'SeedAdGroupIdSearchParameter', 'adGroupId': ad_group_id })
PHP
$seedAdGroupIdSearchParameter = new SeedAdGroupIdSearchParameter(); $seedAdGroupIdSearchParameter->setAdGroupId($adGroupId); $searchParameters[] = $seedAdGroupIdSearchParameter;
Perl
my $seed_ad_group_id_search_parameter = Google::Ads::AdWords::v201809::SeedAdGroupIdSearchParameter->new({ adGroupId => $ad_group_id }); push @search_parameters, $seed_ad_group_id_search_parameter;
Ruby
search_parameters << { :xsi_type => 'SeedAdGroupIdSearchParameter', :ad_group_id => ad_group_id }
VB.NET
Dim seedAdGroupIdSearchParameter As New SeedAdGroupIdSearchParameter() seedAdGroupIdSearchParameter.adGroupId = adGroupId.Value searchParameters.Add(seedAdGroupIdSearchParameter)
Mapping to Keyword Planner
One of the most frequently asked questions about the TargetingIdeaService is: "Why do the TargetingIdeaService results differ from those of the Keyword Planner tool?"
The reason is that a TargetingIdeaSelector
can be configured with more than one different
SearchParameter
,
and the returned results may differ significantly when a different SearchParameter
is used.
Select the right network
One of the most common mistakes is not specifying the
NetworkSearchParameter
.
For example, the Keyword Planner may only be searching keyword ideas for
the Google Search network, but not for other networks. In this case, make sure to
set the NetworkSearchParameter
in the TargetingIdeaSelector
.
For example, to retrieve ideas only for the Search network, set the
NetworkSearchParameter
like this:
Java
// Create network search parameter (optional). NetworkSetting networkSetting = new NetworkSetting(); networkSetting.setTargetGoogleSearch(true); networkSetting.setTargetSearchNetwork(false); networkSetting.setTargetContentNetwork(false); networkSetting.setTargetPartnerSearchNetwork(false); NetworkSearchParameter networkSearchParameter = new NetworkSearchParameter(); networkSearchParameter.setNetworkSetting(networkSetting); searchParameters.add(networkSearchParameter);
C#
// Add network search parameter (optional). NetworkSetting networkSetting = new NetworkSetting { targetGoogleSearch = true, targetSearchNetwork = false, targetContentNetwork = false, targetPartnerSearchNetwork = false }; NetworkSearchParameter networkSearchParameter = new NetworkSearchParameter { networkSetting = networkSetting }; searchParameters.Add(networkSearchParameter);
Python
# Network search parameter (optional) selector['searchParameters'].append({ 'xsi_type': 'NetworkSearchParameter', 'networkSetting': { 'targetGoogleSearch': True, 'targetSearchNetwork': False, 'targetContentNetwork': False, 'targetPartnerSearchNetwork': False } })
PHP
// Create network search parameter (optional). $networkSetting = new NetworkSetting(); $networkSetting->setTargetGoogleSearch(true); $networkSetting->setTargetSearchNetwork(false); $networkSetting->setTargetContentNetwork(false); $networkSetting->setTargetPartnerSearchNetwork(false); $networkSearchParameter = new NetworkSearchParameter(); $networkSearchParameter->setNetworkSetting($networkSetting); $searchParameters[] = $networkSearchParameter;
Perl
# Create network search paramter (optional). my $network_setting = Google::Ads::AdWords::v201809::NetworkSetting->new({ targetGoogleSearch => 1, targetSearchNetwork => 0, targetContentNetwork => 0, targetPartnerSearchNetwork => 0 }); my $network_setting_parameter = Google::Ads::AdWords::v201809::NetworkSearchParameter->new( {networkSetting => $network_setting}); push @search_parameters, $network_setting_parameter;
Ruby
search_parameters << { # Network search parameter (optional). :xsi_type => 'NetworkSearchParameter', :network_setting => { :target_google_search => true, :target_search_network => false, :target_content_network => false, :target_partner_search_network => false } }
VB.NET
' Add network search parameter (optional). Dim networkSetting As New NetworkSetting() networkSetting.targetGoogleSearch = True networkSetting.targetSearchNetwork = False networkSetting.targetContentNetwork = False networkSetting.targetPartnerSearchNetwork = False Dim networkSearchParameter As New NetworkSearchParameter() networkSearchParameter.networkSetting = networkSetting searchParameters.Add(networkSearchParameter)
It's important to configure the NetworkSearchParameter
consistent with how the potential
campaign will be configured.
Search for new keywords using a phrase, website, or category
To implement something similar to this Keyword Planner functionality, each
of the input fields can be mapped to a corresponding TargetingIdeaSelector
configuration (for example, SearchParameter
):
Keyword Planner | AdWords API |
---|---|
Your product or service | RelatedToQuerySearchParameter |
Your landing page | RelatedToUrlSearchParameter |
Your product category | CategoryProductsAndServicesSearchParameter
(refer to our Product and Services Categories taxonomy or IDs) |
Targeting - Location | LocationSearchParameter |
Targeting - Language | LanguageSearchParameter |
Targeting - Network | NetworkSearchParameter |
Date Range (Monthly Search Volume) | Select AttributeType.TARGETED_MONTHLY_SEARCHES |
Keyword Filters - Average monthly searches | SearchVolumeSearchParameter |
Keyword Filters - Competition | CompetitionSearchParameter |
Include/Exclude Keywords | IdeaTextFilterSearchParameter ExcludedKeywordSearchParameter |
Lastly, set the TargetingIdeaSelector
.requestType
to IDEAS
.
Get search volume data and trends
The parameters mapping is similar to other functions of this kind:
Keyword Planner | AdWords API |
---|---|
Enter keywords | RelatedToQuerySearchParameter |
Targeting - Location | LocationSearchParameter |
Targeting - Network | NetworkSearchParameter |
Date Range (Monthly Search Volume) | Select AttributeType.TARGETED_MONTHLY_SEARCHES |
Lastly, set the TargetingIdeaSelector.requestType
to STATS
.
Multiply keyword lists to get new keyword
The AdWords API does not automatically calculate keyword combinations. To
achieve the same functionality, you need to programmatically determine the
combined keywords list, and then set up the TargetingIdeaSelector
accordingly.
Keyword Planner | AdWords API |
---|---|
List 1 and List 2 | Programmatically combine the two lists and then use the
RelatedToQuerySearchParameter |
Targeting - Location | LocationSearchParameter |
Targeting - Network | NetworkSearchParameter |
Code examples
Each client library contains a complete code example in the Optimization folder: