A Shopping campaign is a campaign type that helps promote your products by giving users detailed product information before they even click your ad. Ads in Shopping campaigns show users a photo of your product, plus a title, price, store name, and more.
This guide describes how to set up and manage Shopping campaigns in the AdWords API.
Linking your Merchant Center and Google Ads accounts
Before you can create a Shopping campaign, you need to first link your Google Ads account to your Google Merchant Center account as follows:
- Send an invitation from your Merchant Center account to your Google Ads account.
- Accept the invitation in your Google Ads account.
Send an invitation from your Merchant Center account
You can
use the Merchant Center UI to send the
invitation, or use the
Content API for Shopping to update the
adwordsLinks
of your Account
.
Managing invitations in your Google Ads account
You can use getServiceLinks
and mutateServiceLinks
of
CustomerService to
retrieve, accept, and reject links between your Google Ads account and other
services, including Merchant Center.
To retrieve all links to your account, simply call getServiceLinks
with a
predicate on serviceType = MERCHANT_CENTER
as follows:
Java
// Get the CustomerService. CustomerServiceInterface customerService = adWordsServices.get(session, CustomerServiceInterface.class); // Create a selector that filters by service type. Selector selector = new SelectorBuilder() .fields("ServiceType") .equals("ServiceType", ServiceType.MERCHANT_CENTER.getValue()) .build(); // Get the service links. ServiceLink[] serviceLinks = customerService.getServiceLinks(selector); // Display the results. if (serviceLinks != null) { for (ServiceLink serviceLink : serviceLinks) { System.out.printf( "Found service link with service link ID %d, type %s, name '%s', and status %s.%n", serviceLink.getServiceLinkId(), serviceLink.getServiceType(), serviceLink.getName(), serviceLink.getLinkStatus()); } } else { System.out.println("No service links found."); }
C#
// Get the CustomerService. CustomerService customerService = (CustomerService) user.GetService(AdWordsService.v201809.CustomerService); // Create a selector that filters by service type. Selector selector = new Selector() { fields = new string[] { ServiceLink.Fields.ServiceType }, predicates = new Predicate[] { Predicate.Equals(ServiceLink.Fields.ServiceType, ServiceType.MERCHANT_CENTER.ToString()) } }; // Get the service links. ServiceLink[] serviceLinks = customerService.getServiceLinks(selector); // Display the results. if (serviceLinks != null) { foreach (ServiceLink serviceLink in serviceLinks) { Console.WriteLine("Found service link with service link ID {0}, type {1}, name " + "'{2}', and status {3}.", serviceLink.serviceLinkId, serviceLink.serviceType, serviceLink.name, serviceLink.linkStatus); } } else { Console.WriteLine("No service links found."); }
Python
def main(client): # Initialize appropriate service. customer_service = client.GetService('CustomerService', version='v201809') selector = { 'fields': ['ServiceType'], 'predicates': [ { 'field': 'ServiceType', 'operator': 'EQUALS', 'values': ['MERCHANT_CENTER'] } ] } service_links = customer_service.getServiceLinks(selector) # Display results. if service_links: for service_link in service_links: print('Service link with service link ID \'%s\', type \'%s\', ' 'name \'%s\', and status \'%s\' was found.' % (service_link['serviceLinkId'], service_link['serviceType'], service_link['name'], service_link['linkStatus'])) else: print('No service links were found.') if __name__ == '__main__': # Initialize client object. adwords_client = adwords.AdWordsClient.LoadFromStorage() main(adwords_client)
PHP
$customerService = $adWordsServices->get($session, CustomerService::class); // Create a selector to select all service links. $selector = new Selector(); $selector->setFields(['ServiceType']); $selector->setPredicates( [ new Predicate( 'ServiceType', PredicateOperator::EQUALS, [ServiceType::MERCHANT_CENTER] ) ] ); // Get the service links on the server. $serviceLinks = $customerService->getServiceLinks($selector); // Print out some information about service links. if ($serviceLinks !== null) { foreach ($serviceLinks as $serviceLink) { printf( "Found service link with service link ID %d, type %s, name '%s'," . " and status %s.\n", $serviceLink->getServiceLinkId(), $serviceLink->getServiceType(), $serviceLink->getName(), $serviceLink->getLinkStatus() ); } } else { print "No service links found.\n"; }
Perl
my $client = shift; # Create a selector that filters by service type. my $paging = Google::Ads::AdWords::v201809::Paging->new({ startIndex => 0, numberResults => PAGE_SIZE }); my $selector = Google::Ads::AdWords::v201809::Selector->new({ fields => ["ServiceType"], predicates => [ Google::Ads::AdWords::v201809::Predicate->new({ field => "ServiceType", operator => "EQUALS", values => ["MERCHANT_CENTER"]}) ], paging => $paging }); # Get the service links. my $service_links = $client->CustomerService->getServiceLinks({selector => $selector}); # Display the results. if ($service_links) { foreach my $service_link ($service_links) { printf( "Found service link with service link ID %d," . " type %s, name '%s', and status %s.\n", $service_link->get_serviceLinkId(), $service_link->get_serviceType(), $service_link->get_name(), $service_link->get_linkStatus()); } } else { printf("No service links found.\n"); }
Ruby
# Get the CustomerService. customer_srv = adwords.service(:CustomerService, API_VERSION) # Create a selector that filters by service type. selector = { :fields => ['ServiceType'], :predicates => [{ :field => 'ServiceType', :operator => 'EQUALS', :values => ['MERCHANT_CENTER'] }] } # Get the service links. service_links = customer_srv.get_service_links(selector) # Display the results. if service_links.kind_of?(Array) service_links.each do |service_link| puts ("Found service link with service link ID %d, type %s, name '%s'," + " and status %s.") % [ service_link[:service_link_id], service_link[:service_type], service_link[:name], service_link[:link_status] ] end else puts "No service links found." end
To accept an invitation, call mutateServiceLinks
and pass a SET
operation
that changes the
linkStatus
from PENDING
to ACTIVE
. Make sure you set the serviceType
and the
serviceLinkId
on the operand.
Java
// Get the CustomerService. CustomerServiceInterface customerService = adWordsServices.get(session, CustomerServiceInterface.class); // Create the operation to set the status to ACTIVE. ServiceLinkOperation op = new ServiceLinkOperation(); op.setOperator(Operator.SET); ServiceLink serviceLink = new ServiceLink(); serviceLink.setServiceLinkId(serviceLinkId); serviceLink.setServiceType(ServiceType.MERCHANT_CENTER); serviceLink.setLinkStatus(ServiceLinkLinkStatus.ACTIVE); op.setOperand(serviceLink); // Update the service link. ServiceLink[] mutatedServiceLinks = customerService.mutateServiceLinks(new ServiceLinkOperation[] {op}); // Display the results. for (ServiceLink mutatedServiceLink : mutatedServiceLinks) { System.out.printf( "Service link with service link ID %d, type '%s' updated to status: %s.%n", mutatedServiceLink.getServiceLinkId(), mutatedServiceLink.getServiceType(), mutatedServiceLink.getLinkStatus()); }
C#
using (CustomerService customerService = (CustomerService) user.GetService(AdWordsService.v201809.CustomerService)) { // Create the operation to set the status to ACTIVE. ServiceLinkOperation op = new ServiceLinkOperation { @operator = Operator.SET }; ServiceLink serviceLink = new ServiceLink { serviceLinkId = serviceLinkId, serviceType = ServiceType.MERCHANT_CENTER, linkStatus = ServiceLinkLinkStatus.ACTIVE }; op.operand = serviceLink; try { // Update the service link. ServiceLink[] mutatedServiceLinks = customerService.mutateServiceLinks( new ServiceLinkOperation[] { op }); // Display the results. foreach (ServiceLink mutatedServiceLink in mutatedServiceLinks) { Console.WriteLine( "Service link with service link ID {0}, type '{1}' updated to " + "status: {2}.", mutatedServiceLink.serviceLinkId, mutatedServiceLink.serviceType, mutatedServiceLink.linkStatus); }
Python
def main(client, service_link_id): # Initialize appropriate service. customer_service = client.GetService( 'CustomerService', version='v201809') # Create the operation to set the status to ACTIVE. operations = [{ 'operator': 'SET', 'operand': { 'serviceLinkId': service_link_id, 'serviceType': 'MERCHANT_CENTER', 'linkStatus': 'ACTIVE', } }] # Update the service link. mutated_service_links = customer_service.mutateServiceLinks(operations) # Display results. for mutated_service_link in mutated_service_links: print('Service link with service link ID "%s", type "%s" was updated ' 'to status: "%s".' % (mutated_service_link['serviceLinkId'], mutated_service_link['serviceType'], mutated_service_link['linkStatus']))
PHP
$customerService = $adWordsServices->get($session, CustomerService::class); // Create service link. $serviceLink = new ServiceLink(); $serviceLink->setServiceLinkId($serviceLinkId); $serviceLink->setServiceType(ServiceType::MERCHANT_CENTER); $serviceLink->setLinkStatus(ServiceLinkLinkStatus::ACTIVE); // Create a service link operation and add it to the list. $operations = []; $operation = new ServiceLinkOperation(); $operation->setOperator(Operator::SET); $operation->setOperand($serviceLink); $operations[] = $operation; // Accept service links on the server and print out some information about // accepted service links. $serviceLinks = $customerService->mutateServiceLinks($operations); foreach ($serviceLinks as $serviceLink) { printf( "Service link with service link ID %d and type '%s' updated to status: %s.\n", $serviceLink->getServiceLinkId(), $serviceLink->getServiceType(), $serviceLink->getLinkStatus() ); }
Perl
my $client = shift; my $service_link_id = shift; my $service_link = Google::Ads::AdWords::v201809::ServiceLink->new({ serviceLinkId => $service_link_id, serviceType => "MERCHANT_CENTER", linkStatus => "ACTIVE" }); # Create the operation to set the status to ACTIVE. my $op = Google::Ads::AdWords::v201809::ServiceLinkOperation->new({ operator => "SET", operand => $service_link }); # Update the service link. my $mutated_service_links = $client->CustomerService->mutateServiceLinks({operations => [$op]}); # Display the results. foreach my $mutated_service_link ($mutated_service_links) { printf( "Service link with service link ID %d, " . "type '%s' updated to status: %s.\n", $mutated_service_link->get_serviceLinkId(), $mutated_service_link->get_serviceType(), $mutated_service_link->get_linkStatus()); }
Ruby
# Get the CustomerService. customer_srv = adwords.service(:CustomerService, API_VERSION) # Create the operation to set the status to ACTIVE. operation = { :operator => 'SET', :operand => { :service_link_id => service_link_id, :service_type => 'MERCHANT_CENTER', :link_status => 'ACTIVE' } } # Update the service link. mutated_service_links = customer_srv.mutate_service_links([operation]) # Display the results. mutated_service_links.each do |mutated_service_link| puts ("Service link with service link ID %d, type '%s' updated to status:" + "%s.") % [ mutated_service_link[:service_link_id], mutated_service_link[:service_type], mutated_service_link[:link_status] ] end
VB.NET
Using customerService As CustomerService = CType( user.GetService( AdWordsService.v201809.CustomerService), CustomerService) ' Create the operation to set the status to ACTIVE. Dim op As New ServiceLinkOperation() op.operator = [Operator].SET Dim serviceLink As New ServiceLink() serviceLink.serviceLinkId = serviceLinkId serviceLink.serviceType = ServiceType.MERCHANT_CENTER serviceLink.linkStatus = ServiceLinkLinkStatus.ACTIVE op.operand = serviceLink Try ' Update the service link. Dim mutatedServiceLinks As ServiceLink() = customerService.mutateServiceLinks(New ServiceLinkOperation() {op}) ' Display the results. For Each mutatedServiceLink As ServiceLink In mutatedServiceLinks Console.WriteLine( "Service link with service link ID {0}, type '{1}' updated to " & "status: {2}.", mutatedServiceLink.serviceLinkId, mutatedServiceLink.serviceType, mutatedServiceLink.linkStatus) Next
To reject an invitation, call mutateServiceLinks
and pass a REMOVE
operation with an operand that has serviceType
and serviceLinkId
set.
Java
// Get the CustomerService. CustomerServiceInterface customerService = adWordsServices.get(session, CustomerServiceInterface.class); // Create the operation to remove the service link. ServiceLinkOperation op = new ServiceLinkOperation(); op.setOperator(Operator.REMOVE); ServiceLink serviceLink = new ServiceLink(); serviceLink.setServiceLinkId(serviceLinkId); serviceLink.setServiceType(ServiceType.MERCHANT_CENTER); op.setOperand(serviceLink); // Remove the service link. ServiceLink[] mutatedServiceLinks = customerService.mutateServiceLinks(new ServiceLinkOperation[] {op}); // Display the results. for (ServiceLink mutatedServiceLink : mutatedServiceLinks) { System.out.printf( "Service link with service link ID %d and type '%s' was removed.%n", mutatedServiceLink.getServiceLinkId(), mutatedServiceLink.getServiceType()); }
C#
// Get the CustomerService. CustomerService customerService = (CustomerService) user.GetService(AdWordsService.v201809.CustomerService); // Create the operation to remove the service link. ServiceLinkOperation op = new ServiceLinkOperation(); op.@operator = Operator.REMOVE; ServiceLink serviceLink = new ServiceLink(); serviceLink.serviceLinkId = serviceLinkId; serviceLink.serviceType = ServiceType.MERCHANT_CENTER; op.operand = serviceLink; // Remove the service link. ServiceLink[] mutatedServiceLinks = customerService.mutateServiceLinks( new ServiceLinkOperation[] { op }); // Display the results. foreach (ServiceLink mutatedServiceLink in mutatedServiceLinks) { Console.WriteLine("Service link with service link ID {0} and type '{1}' was removed.", mutatedServiceLink.serviceLinkId, mutatedServiceLink.serviceType); }
Python
def main(client, service_link_id): # Initialize appropriate service. customer_service = client.GetService( 'CustomerService', version='v201809') # Create the operation to set the status to ACTIVE. operations = [{ 'operator': 'REMOVE', 'operand': { 'serviceLinkId': service_link_id, 'serviceType': 'MERCHANT_CENTER', } }] # Remove the service link. mutated_service_links = customer_service.mutateServiceLinks(operations) # Display results. for mutated_service_link in mutated_service_links: print('Service link with service link ID \'%s\' and type \'%s\' was ' 'removed.' % (mutated_service_link['serviceLinkId'], mutated_service_link['serviceType']))
PHP
$customerService = $adWordsServices->get($session, CustomerService::class); // Create service link. $serviceLink = new ServiceLink(); $serviceLink->setServiceLinkId($serviceLinkId); $serviceLink->setServiceType(ServiceType::MERCHANT_CENTER); // Create a service link operation and add it to the list. $operations = []; $operation = new ServiceLinkOperation(); $operation->setOperator(Operator::REMOVE); $operation->setOperand($serviceLink); $operations[] = $operation; // Reject service links on the server and print out some information about // rejected service links. $serviceLinks = $customerService->mutateServiceLinks($operations); foreach ($serviceLinks as $serviceLink) { printf( "Service link with service link ID %d and type '%s' was removed.\n", $serviceLink->getServiceLinkId(), $serviceLink->getServiceType() ); }
Perl
my $client = shift; my $service_link_id = shift; my $service_link = Google::Ads::AdWords::v201809::ServiceLink->new({ serviceLinkId => $service_link_id, serviceType => "MERCHANT_CENTER" }); # Create the operation to remove the service link. my $op = Google::Ads::AdWords::v201809::ServiceLinkOperation->new({ operator => "REMOVE", operand => $service_link }); # Remove the service link. my $mutated_service_links = $client->CustomerService->mutateServiceLinks({operations => [$op]}); # Display the results. foreach my $mutated_service_link ($mutated_service_links) { printf( "Service link with service link ID %d and type '%s' was removed.\n", $mutated_service_link->get_serviceLinkId(), $mutated_service_link->get_serviceType()); }
Ruby
# Get the CustomerService. customer_srv = adwords.service(:CustomerService, API_VERSION) # Create the operation to remove the service link. operation = { :operator => 'REMOVE', :operand => { :service_link_id => service_link_id, :service_type => 'MERCHANT_CENTER' } } # Update the service link. mutated_service_links = customer_srv.mutate_service_links([operation]) # Display the results. mutated_service_links.each do |mutated_service_link| puts "Service link with service link ID %d, type '%s' was removed." % [ mutated_service_link[:service_link_id], mutated_service_link[:service_type] ] end
Creating a Shopping campaign
Standard Shopping campaigns
When creating a Shopping campaign, you set the budgets, bids, and settings. The simplest type of Shopping campaign has one bid for all products.
There are two steps unique to setting up a Shopping campaign:
Set the campaign's
advertisingChannelType
toSHOPPING
.Create a
ShoppingSetting
and add it to the campaign.
Both steps are demonstrated in code below:
Java
// Create campaign. Campaign campaign = new Campaign(); campaign.setName("Shopping campaign #" + System.currentTimeMillis()); // The advertisingChannelType is what makes this a Shopping campaign campaign.setAdvertisingChannelType(AdvertisingChannelType.SHOPPING); // Recommendation: Set the campaign to PAUSED when creating it to prevent // the ads from immediately serving. Set to ENABLED once you've added // targeting and the ads are ready to serve. campaign.setStatus(CampaignStatus.PAUSED); // Set shared budget (required). Budget budget = new Budget(); budget.setBudgetId(budgetId); campaign.setBudget(budget); // Set bidding strategy (required). BiddingStrategyConfiguration biddingStrategyConfiguration = new BiddingStrategyConfiguration(); biddingStrategyConfiguration.setBiddingStrategyType(BiddingStrategyType.MANUAL_CPC); campaign.setBiddingStrategyConfiguration(biddingStrategyConfiguration); // All Shopping campaigns need a ShoppingSetting. ShoppingSetting shoppingSetting = new ShoppingSetting(); shoppingSetting.setSalesCountry("US"); shoppingSetting.setCampaignPriority(0); shoppingSetting.setMerchantId(merchantId); // Set to 'true' to enable Local Inventory Ads in your campaign. shoppingSetting.setEnableLocal(true); campaign.setSettings(new Setting[] {shoppingSetting}); // Create operation. CampaignOperation campaignOperation = new CampaignOperation(); campaignOperation.setOperand(campaign); campaignOperation.setOperator(Operator.ADD); // Make the mutate request. CampaignReturnValue campaignAddResult = campaignService.mutate(new CampaignOperation[] {campaignOperation}); // Display result. campaign = campaignAddResult.getValue(0); System.out.printf("Campaign with name '%s' and ID %d was added.%n", campaign.getName(), campaign.getId());
Python
def main(client, budget_id, merchant_id, create_default_partition): campaign_service = client.GetService('CampaignService', version='v201809') ad_group_service = client.GetService('AdGroupService', version='v201809') ad_group_ad_service = client.GetService('AdGroupAdService', version='v201809') # Create campaign campaign = { 'name': 'Shopping campaign #%s' % uuid.uuid4(), # The advertisingChannelType is what makes this a shopping campaign 'advertisingChannelType': 'SHOPPING', # Recommendation: Set the campaign to PAUSED when creating it to stop the # ads from immediately serving. Set to ENABLED once you've added targeting # and the ads are ready to serve. 'status': 'PAUSED', # Set portfolio budget (required) 'budget': { 'budgetId': budget_id }, 'biddingStrategyConfiguration': { 'biddingStrategyType': 'MANUAL_CPC' }, 'settings': [ # All shopping campaigns need a ShoppingSetting { 'xsi_type': 'ShoppingSetting', 'salesCountry': 'US', 'campaignPriority': '0', 'merchantId': merchant_id, # Set to "True" to enable Local Inventory Ads in your campaign. 'enableLocal': True } ] } campaign_operations = [{ 'operator': 'ADD', 'operand': campaign }] result = campaign_service.mutate(campaign_operations) for campaign in result['value']: print('Campaign with name "%s" and ID "%s" was added.' % (campaign['name'], campaign['id'])) # Create the AdGroup ad_group = { 'campaignId': campaign['id'], 'name': 'AdGroup #%s' % uuid.uuid4() } adgroup_operations = { 'operator': 'ADD', 'operand': ad_group } # Make the mutate request to add the AdGroup to the Shopping Campaign ad_group = ad_group_service.mutate(adgroup_operations)['value'][0] ad_group_id = ad_group['id'] print('AdGroup with name "%s" and ID "%s" was added.' % (ad_group['name'], ad_group_id)) # Create an AdGroup Ad adgroup_ad = { 'adGroupId': ad_group_id, # Create ProductAd 'ad': { 'xsi_type': 'ProductAd', 'Ad.Type': 'ProductAd' } } ad_operation = { 'operator': 'ADD', 'operand': adgroup_ad } # Make the mutate request to add the ProductAd to the AdGroup ad_result = ad_group_ad_service.mutate([ad_operation]) for adgroup_ad in ad_result['value']: print('ProductAd with ID "%s" was added.' % adgroup_ad['ad']['id']) if create_default_partition: CreateDefaultPartition(client, ad_group_id)
PHP
// Create a campaign with required and optional settings. $campaign = new Campaign(); $campaign->setName('Shopping campaign #' . uniqid()); // The advertisingChannelType is what makes this a Shopping campaign $campaign->setAdvertisingChannelType(AdvertisingChannelType::SHOPPING); // Recommendation: Set the campaign to PAUSED when creating it to stop // the ads from immediately serving. Set to ENABLED once you've added // targeting and the ads are ready to serve. $campaign->setStatus(CampaignStatus::PAUSED); // Set portfolio budget (required). $campaign->setBudget(new Budget()); $campaign->getBudget()->setBudgetId($budgetId); // Set bidding strategy (required). $biddingStrategyConfiguration = new BiddingStrategyConfiguration(); $biddingStrategyConfiguration->setBiddingStrategyType( BiddingStrategyType::MANUAL_CPC ); $campaign->setBiddingStrategyConfiguration($biddingStrategyConfiguration); // All Shopping campaigns need a ShoppingSetting. $shoppingSetting = new ShoppingSetting(); $shoppingSetting->setSalesCountry('US'); $shoppingSetting->setCampaignPriority(0); $shoppingSetting->setMerchantId($merchantId); // Set to "true" to enable Local Inventory Ads in your campaign. $shoppingSetting->setEnableLocal(true); $campaign->setSettings([$shoppingSetting]); // Create a campaign operation and add it to the operations list. $operations = []; $operation = new CampaignOperation(); $operation->setOperand($campaign); $operation->setOperator(Operator::ADD); $operations[] = $operation; // Create the campaign on the server and print out some information. $campaign = $campaignService->mutate($operations)->getValue()[0]; printf( "Campaign with name '%s' and ID %d was added.\n", $campaign->getName(), $campaign->getId() );
Perl
my $campaign = Google::Ads::AdWords::v201809::Campaign->new({ name => "Shopping campaign #" . uniqid(), # The advertisingChannelType is what makes this a Shopping campaign advertisingChannelType => "SHOPPING", # Recommendation: Set the campaign to PAUSED when creating it to stop # the ads from immediately serving. Set to ENABLED once you've added # targeting and the ads are ready to serve. status => "PAUSED", # Set budget (required) budget => Google::Ads::AdWords::v201809::Budget->new({budgetId => $budget_id}), # Set bidding strategy (required) biddingStrategyConfiguration => Google::Ads::AdWords::v201809::BiddingStrategyConfiguration->new( {biddingStrategyType => "MANUAL_CPC"} ), # Set shopping setting (required) settings => [ # All Shopping campaigns need a ShoppingSetting Google::Ads::AdWords::v201809::ShoppingSetting->new({ salesCountry => "US", campaignPriority => 0, merchantId => $merchant_id, # By setting enableLocal to true (1) below, you will enable Local # Inventory Ads in your campaign. Set this to false (0) if you want # to disable this feature in your campaign. enableLocal => 1 })]});
Ruby
# Create campaign. campaign = { :name => "Shopping campaign #%d" % (Time.new.to_f * 1000).to_i, # The advertising_channel_type is what makes this a Shopping campaign. :advertising_channel_type => 'SHOPPING', # Recommendation: Set the campaign to PAUSED when creating it to stop the # ads from immediately serving. Set to ENABLED once you've added # targeting and the ads are ready to serve. :status => 'PAUSED', :budget => {:budget_id => budget_id}, :bidding_strategy_configuration => { :bidding_strategy_type => 'MANUAL_CPC' }, :settings => [ { :xsi_type => 'ShoppingSetting', :sales_country => 'US', :campaign_priority => 0, :merchant_id => merchant_id, # Set to "true" to enable Local Inventory Ads in your campaign. :enable_local => true } ] } campaign_operation = {:operator => 'ADD', :operand => campaign} # Make the mutate request. result = campaign_srv.mutate([campaign_operation])
VB.NET
''' <summary> ''' Creates the shopping campaign. ''' </summary> ''' <param name="user">The AdWords user.</param> ''' <param name="budgetId">The budget id.</param> ''' <param name="merchantId">The Merchant Center id.</param> ''' <returns>The Shopping campaign.</returns> Private Function CreateCampaign(ByVal user As AdWordsUser, ByVal budgetId As Long, ByVal merchantId As Long) As Campaign ' Get the required services. Dim campaignService As CampaignService = CType( user.GetService( AdWordsService.v201809.CampaignService), CampaignService) ' Create campaign. Dim campaign As New Campaign() campaign.name = "Shopping campaign #" & ExampleUtilities.GetRandomString() ' The advertisingChannelType is what makes this a Shopping campaign. campaign.advertisingChannelType = AdvertisingChannelType.SHOPPING ' Recommendation: Set the campaign to PAUSED when creating it to prevent ' the ads from immediately serving. Set to ENABLED once you've added ' targeting and the ads are ready to serve. campaign.status = CampaignStatus.PAUSED ' Set shared budget (required). campaign.budget = New Budget() campaign.budget.budgetId = budgetId ' Set bidding strategy (required). Dim biddingStrategyConfiguration As New BiddingStrategyConfiguration() biddingStrategyConfiguration.biddingStrategyType = BiddingStrategyType.MANUAL_CPC campaign.biddingStrategyConfiguration = biddingStrategyConfiguration ' All Shopping campaigns need a ShoppingSetting. Dim shoppingSetting As New ShoppingSetting() shoppingSetting.salesCountry = "US" shoppingSetting.campaignPriority = 0 shoppingSetting.merchantId = merchantId ' Enable Local Inventory Ads in your campaign. shoppingSetting.enableLocal = True campaign.settings = New Setting() {shoppingSetting} ' Create operation. Dim campaignOperation As New CampaignOperation() campaignOperation.operand = campaign campaignOperation.operator = [Operator].ADD ' Make the mutate request. Dim retval As CampaignReturnValue = campaignService.mutate( New CampaignOperation() {campaignOperation}) Return retval.value(0) End Function
As shown in the sample above, a ShoppingSetting
has the following properties:
merchantId
(required)- The Merchant Center Account ID that your products belong to.
salesCountry
(required for Shopping campaigns)- Only products with a matching target country in Merchant Center will be selected. Note that this does not affect ads targeting.
campaignPriority
(required for all Shopping campaigns except Smart Shopping campaigns; optional, default:3
for Smart Shopping campaigns)- Either
0
,1
,2
, or3
. The value3
is reserved for Smart Shopping campaigns, and the API will automatically set that value if not specified. This field determines which campaign will take priority when more than one campaign uses the same Merchant Center data feed. The campaign with the higher priority will be selected. If two campaigns have the same priority, then each ad will be covered by the one that sets the highest bid for the product. enableLocal
(optional, default:false
)- If set to
true
, ads for products sold in local stores will be enabled in your Shopping campaigns.
Smart Shopping campaigns
The steps for creating a Smart Shopping campaign are similar to those for creating a standard Shopping campaign except for the following:
- Smart Shopping campaigns don't support shared budgets, so you need to set
the budget's
isExplicitlyShared
property tofalse
. - Smart Shopping campaigns require setting the
advertisingChannelSubType
toSHOPPING_GOAL_OPTIMIZED_ADS
. - Smart Shopping campaigns support only
MAXIMIZE_CONVERSION_VALUE
as a bidding strategy type andMaximizeConversionValueBiddingScheme
as a bidding scheme. campaignPriority
is optional. The API will set it to3
for you by default.enableLocal
is optional. The API will set tofalse
for you by default. If set totrue
, ads for products sold in local stores will be enabled in your Shopping campaigns.
You can see the code for creating a Smart Shopping campaign below:
PHP
private static function createSmartCampaign( AdWordsServices $adWordsServices, AdWordsSession $session, $budgetId, $merchantId ) { $campaignService = $adWordsServices->get($session, CampaignService::class); // Create a campaign with required and optional settings. $campaign = new Campaign(); $campaign->setName('Smart Shopping campaign #' . uniqid()); // The advertisingChannelType is what makes this a Shopping campaign. $campaign->setAdvertisingChannelType(AdvertisingChannelType::SHOPPING); // Sets the advertisingChannelSubType to SHOPPING_GOAL_OPTIMIZED_ADS to // make this a Smart Shopping campaign. $campaign->setAdvertisingChannelSubType( AdvertisingChannelSubType::SHOPPING_GOAL_OPTIMIZED_ADS ); // Recommendation: Set the campaign to PAUSED when creating it to stop // the ads from immediately serving. Set to ENABLED once you've added // targeting and the ads are ready to serve. $campaign->setStatus(CampaignStatus::PAUSED); // Set a budget. $budget = new Budget(); $budget->setBudgetId($budgetId); $campaign->setBudget($budget); // Set a bidding strategy. Only MAXIMIZE_CONVERSION_VALUE is supported. $biddingStrategyConfiguration = new BiddingStrategyConfiguration(); $biddingStrategyConfiguration->setBiddingStrategyType( BiddingStrategyType::MAXIMIZE_CONVERSION_VALUE ); $campaign->setBiddingStrategyConfiguration( $biddingStrategyConfiguration ); // All Shopping campaigns need a ShoppingSetting. $shoppingSetting = new ShoppingSetting(); $shoppingSetting->setSalesCountry('US'); $shoppingSetting->setMerchantId($merchantId); $campaign->setSettings([$shoppingSetting]); // Create a campaign operation and add it to the operations list. $operation = new CampaignOperation(); $operation->setOperand($campaign); $operation->setOperator(Operator::ADD); // Create the campaign on the server and print out some information. $addedCampaign = $campaignService->mutate([$operation])->getValue()[0]; printf( "Smart Shopping campaign with name '%s' and ID %d was added.%s", $addedCampaign->getName(), $addedCampaign->getId(), PHP_EOL ); return $addedCampaign->getId(); }
Creating a Shopping ad group and ads
Standard Shopping campaigns
In order to serve ads for your Shopping campaign, you must create an
AdGroup
with at least one ad in the ad group.
Standard Shopping campaigns should have the SHOPPING_PRODUCT_ADS
ad group
type. This is the default ad group type for Shopping campaigns, and serves
standard product ads. The type of an ad group cannot be changed once created.
Once you've created the required type of ad group, complete the basic setup of
your campaign by adding a
ProductAd
to
your ad group.
Next, you need to decide which products to include and how much to bid for them.
Smart Shopping campaigns
In order to serve ads for your Smart Shopping campaign, you must create an
AdGroup
with exactly one ad in the ad group. Set the
adGroupType
field of your ad group to SHOPPING_GOAL_OPTIMIZED_ADS
).
Refer to the code snippets below for details.
PHP
private static function createSmartShoppingAdGroup( AdWordsServices $adWordsServices, AdWordsSession $session, $campaignId ) { $adGroupService = $adWordsServices->get($session, AdGroupService::class); // Create an ad group. $adGroup = new AdGroup(); $adGroup->setCampaignId($campaignId); $adGroup->setName('Smart Shopping ad group #' . uniqid()); // Set the ad group type to SHOPPING_GOAL_OPTIMIZED_ADS. $adGroup->setAdGroupType(AdGroupType::SHOPPING_GOAL_OPTIMIZED_ADS); // Create operation. $adGroupOperation = new AdGroupOperation(); $adGroupOperation->setOperand($adGroup); $adGroupOperation->setOperator(Operator::ADD); // Create the ad group on the server and print out some information. $addedAdGroup = $adGroupService->mutate([$adGroupOperation]) ->getValue()[0]; printf( "Smart Shopping ad group with name '%s' and ID %d was added.%s", $addedAdGroup->getName(), $addedAdGroup->getId(), PHP_EOL ); return $addedAdGroup->getId(); }
Once you create the required type of ad group, complete the basic setup of your
campaign by adding a
GoalOptimizedShoppingAd
to your ad group.
Refer to the example code below:
PHP
private static function createSmartShoppingAd( AdWordsServices $adWordsServices, AdWordsSession $session, $adGroupId ) { $adGroupAdService = $adWordsServices->get($session, AdGroupAdService::class); // Create a Smart Shopping ad (Goal-optimized Shopping ad). $smartShoppingAd = new GoalOptimizedShoppingAd(); // Create an ad group ad. $adGroupAd = new AdGroupAd(); $adGroupAd->setAdGroupId($adGroupId); $adGroupAd->setAd($smartShoppingAd); // Create an ad group ad operation and add it to the operations list. $operation = new AdGroupAdOperation(); $operation->setOperand($adGroupAd); $operation->setOperator(Operator::ADD); // Create the ad group ad on the server and print out some information. $addedAdGroupAd = $adGroupAdService->mutate([$operation]) ->getValue()[0]; printf( "Smart Shopping ad with ID %d was added.%s", $addedAdGroupAd->getAd()->getId(), PHP_EOL ); }
Partitioning
The full power of Shopping campaigns comes from dividing your product inventory along multiple dimensions and working with product groups individually. Consider the diagram below, where the products have been broadly divided as Electronics (which is further divided by brand), Toys and "Everything else" (which is further divided by Used status).
You create this structure as a tree. Each level of the tree represents a partition.
Each node in the tree is either a subdivision or a unit. A subdivision introduces a new level in the tree, while units are leaves of the tree. Each subdivision must always be completely partitioned, so it must contain a unit type representing Other. In the example, the root, Category: Electronics and Category: (Other) nodes are subdivisions.
Nodes are objects of the
ProductPartition
class, a subclass of the
Criterion
class. ProductPartition
objects are linked to the
AdGroup
with either
BiddableAdGroupCriterion
or
NegativeAdGroupCriterion
.
The combination of ProductPartition
type and AdGroupCriterion
type produces
the following effects:
ProductPartition Type | AdGroupCriterion Type | Effect |
---|---|---|
UNIT | BiddableAdGroupCriterion |
A bid for items matching this leaf of the tree. |
UNIT | NegativeAdGroupCriterion |
This combination indicates that you do not wish to bid for this particular leaf in this ad group, but that other ad groups and (lower priority) campaigns may do so. |
Other combinations result in an error.
A ProductPartition
also has a caseValue
which can be any of several
subclasses of the
ProductDimension
type. A ProductDimension
represents the values associated with your products,
such as brand, category, or condition. A full description of the available
ProductDimension
types is available in the
reference documentation.
Each immediate child of a subdivision must have a caseValue
of the same
ProductDimension
subtype. Only the root node doesn't have a caseValue
.
ProductPartition root = new ProductPartition();
root.setPartitionType(ProductPartitionType.SUBDIVISION);
root.setId(-1);
ProductPartition node1 = new ProductPartition();
node1.setPartitionType(ProductPartitionType.UNIT);
node1.setCaseValue(new ProductBrand(null, "Brand A"));
node1.setParentCriterionId(root.getId());
Remember that each subdivision must contain an "empty" caseValue
of the
correct type, representing "all other values".
ProductPartition node2 = new ProductPartition();
node2.setPartitionType(ProductPartitionType.UNIT);
node2.setCaseValue(new ProductBrand());
node2.setParentCriterionId(root.getId());
Temporary IDs
Criteria are not assigned IDs until the mutate request that creates them is
processed by the server. However, a ProductPartition
is invalid until it is
complete, so whenever you create a subdivision you must also create at least
one of its children in the same operation.
To allow you to set the parentCriterionId
for the child nodes, you can use
temporary criterion IDs. These are locally unique (rather than globally unique)
identifiers that apply only within the context of a single mutate request.
Any negative integer can be used as a temporary ID. In the sample code above,
the ID of the root ProductPartition
is set to -1
.
When the request is processed, each Criterion
is assigned a positive
global ID as usual.
Product dimensions
The following ProductDimension
types are available for Shopping campaigns:
ProductBiddingCategory
ProductBrand
ProductCanonicalCondition
ProductCustomAttribute
ProductOfferId
ProductType
ProductChannel
(not supported in Smart Shopping campaigns)ProductChannelExclusivity
(not supported in Smart Shopping campaigns)
ProductBiddingCategory
, ProductCustomAttribute
, and ProductType
are
further split into subtypes. When subdividing by one of these types, each
caseValue
for that subdivision must also be of the same subtype.
ProductBiddingCategory
and ProductType
are hierarchical, with the subtypes
representing depth within the hierarchy. The category Media is a
BIDDING_CATEGORY_L1
while Books is a BIDDING_CATEGORY_L2
and has
Media as its parent. You cannot subdivide by a caseValue
unless the parent
has already been subdivided further up the tree.
For example, you cannot have the Books category as a node directly under the root, nor can Books be directly under Electronics, since Electronics is not the parent of Books.
ProductCustomAttribute
is not hierarchical. Instead, the subtypes
CUSTOM_ATTRIBUTE_0
through CUSTOM_ATTRIBUTE_4
map to the values
custom attribute 0 to custom attribute 4 in Merchant Center.
You can specify local, online, or both channels through the product
dimensions
ProductChannel
and
ProductChannelExclusivity
.
Bidding categories
The values for the ProductBiddingCategory
type are fixed IDs. You can retrieve
the full set of bidding categories from the ConstantDataService
method
getProductBiddingCategoryData
. We highly recommended that you cache the
results of this call as the data set is large and changes infrequently.
Modifying existing ProductPartition trees
The tree must always remain complete, so any mutate operation that would make a
tree invalid must be part of the same request as an operation that fixes it
again. To be valid, all child nodes of a subdivision must have a caseValue
of
the same type, and every subdivision must contain an "other" node.
If you wish to further subdivide a leaf node you must remove the existing unit
and replace it with a subdivision with the same caseValue
. You can then add
your refinements as children of the new node.
Removing a subdivision causes all of its children to be recursively removed too.
Filter by product dimensions
You can add a
ProductScope
criterion to filter the products in a campaign. You can create at most one
ProductScope
per campaign. A ProductScope
is a container for one or more
ProductDimension
s that represent a simple filter on one aspect of a product.
For example, if you add a ProductBrand
subtype of ProductDimension
and set
its value to Nexus
, the campaign will apply only to products that have
"Nexus" set as the brand in Merchant Center.
ProductScope productScope = new ProductScope();
productScope.setDimensions(new ProductDimension[] {new ProductBrand(null, "Nexus")});
You can add up to seven ProductDimension
objects to a ProductScope
. Products
must match all of the given ProductDimension
s to be included in the campaign.
Bidding restrictions
The bidding restrictions on a Shopping ad group varies based on the ad group type. The following table summarizes the differences.
SHOPPING_PRODUCT_ADS | SHOPPING_GOAL_OPTIMIZED_ADS | |
---|---|---|
Bidding Strategy | Only the following types are supported:
|
Only MAXIMIZE_CONVERSION_VALUE is supported. |
Bids at product partition node level | Required | Not allowed |
Reporting
Shopping campaigns introduce two new reports, a
Product Partition report
and a
Shopping Performance report.
Smart Shopping campaigns don't support network segmentation. So,
AdNetworkType1,
AdNetworkType2,
and Slot for them will
always be returned as MIXED
or "Cross-network".
Consult the Reporting guide for more information.