Share your feedback about the Google Ads (AdWords) API. Take the 2021 AdWords API and Google Ads API Annual Survey.

The AdWords API will sunset on April 27, 2022. Migrate to the Google Ads API to take advantage of the latest Google Ads features.

Shopping Campaign Samples

The code samples below provide examples for managing Shopping campaigns using the AdWords API. Client Library.

Build a product partition tree for an ad group

#!/usr/bin/env python
#
# Copyright 2016 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""This example creates a ProductPartition tree.

The LoadFromStorage method is pulling credentials and properties from a
"googleads.yaml" file. By default, it looks for this file in your home
directory. For more information, see the "Caching authentication information"
section of our README.

"""


# Import appropriate modules from the client library.
from googleads import adwords

ADGROUP_ID = 'INSERT_AD_GROUP_ID_HERE'


class ProductPartitionHelper(object):
  """A helper for creating ProductPartition trees."""

  def __init__(self, adgroup_id):
    """Initializer.

    Args:
      adgroup_id: The ID of the AdGroup that we wish to attach the partition
                  tree to.
    """
    # The next temporary criterion ID to be used.
    # When creating our tree we need to specify the parent-child relationships
    # between nodes. However, until a criterion has been created on the server
    # we do not have a criterion ID with which to refer to it.
    # Instead we can specify temporary IDs that are specific to a single mutate
    # request. Once the criteria have been created they are assigned an ID as
    # normal and the temporary ID will no longer refer to it.
    # A valid temporary ID is any negative integer.
    self.next_id = -1
    # The set of mutate operations needed to create the current tree.
    self.operations = []
    self.adgroup_id = adgroup_id

  def CreateSubdivision(self, parent=None, value=None):
    """Creates a subdivision node.

    Args:
      parent: The node that should be this node's parent.
      value: The value being partitioned on.
    Returns:
      A new subdivision node.
    """
    division = {
        'xsi_type': 'ProductPartition',
        'partitionType': 'SUBDIVISION',
        'id': str(self.next_id)
    }

    # The root has neither a parent nor a value.
    if parent is not None:
      division['parentCriterionId'] = parent['id']
      division['caseValue'] = value

    adgroup_criterion = {
        'xsi_type': 'BiddableAdGroupCriterion',
        'adGroupId': self.adgroup_id,
        'criterion': division
    }

    self.CreateAddOperation(adgroup_criterion)
    self.next_id -= 1

    return division

  def CreateUnit(self, parent=None, value=None, bid_amount=None):
    """Creates a unit node.

    Args:
      parent: The node that should be this node's parent.
      value: The value being partitioned on.
      bid_amount: The amount to bid for matching products, in micros.
    Returns:
      A new unit node.
    """
    unit = {
        'xsi_type': 'ProductPartition',
        'partitionType': 'UNIT'
    }

    # The root node has neither a parent nor a value.
    if parent is not None:
      unit['parentCriterionId'] = parent['id']
      unit['caseValue'] = value

    if bid_amount is not None and bid_amount > 0:
      bidding_strategy_configuration = {
          'bids': [{
              'xsi_type': 'CpcBid',
              'bid': {
                  'xsi_type': 'Money',
                  'microAmount': str(bid_amount)
              }
          }]
      }

      adgroup_criterion = {
          'xsi_type': 'BiddableAdGroupCriterion',
          'biddingStrategyConfiguration': bidding_strategy_configuration
      }
    else:
      adgroup_criterion = {
          'xsi_type': 'NegativeAdGroupCriterion'
      }

    adgroup_criterion['adGroupId'] = self.adgroup_id
    adgroup_criterion['criterion'] = unit

    self.CreateAddOperation(adgroup_criterion)

    return unit

  def GetOperations(self):
    """Returns the set of mutate operations needed to create the current tree.

    Returns:
      The set of operations
    """
    return self.operations

  def CreateAddOperation(self, criterion):
    """Creates an AdGroupCriterionOperation for the given criterion.

    Args:
      criterion: The criterion we want to add.
    """
    operation = {
        'operator': 'ADD',
        'operand': criterion
    }

    self.operations.append(operation)


def main(client, adgroup_id):
  """Runs the example."""
  adgroup_criterion_service = client.GetService(
      'AdGroupCriterionService', version='v201809')

  helper = ProductPartitionHelper(adgroup_id)

  # The most trivial partition tree has only a unit node as the root, e.g.:
  # helper.CreateUnit(bid_amount=100000)

  root = helper.CreateSubdivision()

  new_product_canonical_condition = {
      'xsi_type': 'ProductCanonicalCondition',
      'condition': 'NEW'
  }

  used_product_canonical_condition = {
      'xsi_type': 'ProductCanonicalCondition',
      'condition': 'USED'
  }

  other_product_canonical_condition = {
      'xsi_type': 'ProductCanonicalCondition',
  }

  helper.CreateUnit(root, new_product_canonical_condition, 200000)
  helper.CreateUnit(root, used_product_canonical_condition, 100000)
  other_condition = helper.CreateSubdivision(
      root, other_product_canonical_condition)

  cool_product_brand = {
      'xsi_type': 'ProductBrand',
      'value': 'CoolBrand'
  }

  cheap_product_brand = {
      'xsi_type': 'ProductBrand',
      'value': 'CheapBrand'
  }

  other_product_brand = {
      'xsi_type': 'ProductBrand',
  }

  helper.CreateUnit(other_condition, cool_product_brand, 900000)
  helper.CreateUnit(other_condition, cheap_product_brand, 10000)
  other_brand = helper.CreateSubdivision(other_condition, other_product_brand)

  # The value for the bidding category is a fixed ID for the 'Luggage & Bags'
  # category. You can retrieve IDs for categories from the ConstantDataService.
  # See the 'GetProductTaxonomy' example for more details.
  luggage_category = {
      'xsi_type': 'ProductBiddingCategory',
      'type': 'BIDDING_CATEGORY_L1',
      'value': '-5914235892932915235'
  }

  generic_category = {
      'xsi_type': 'ProductBiddingCategory',
      'type': 'BIDDING_CATEGORY_L1',
  }

  helper.CreateUnit(other_brand, luggage_category, 750000)
  helper.CreateUnit(other_brand, generic_category, 110000)

  # Make the mutate request
  result = adgroup_criterion_service.mutate(helper.GetOperations())

  children = {}

  root_node = None

  # For each criterion, make an array containing each of its children.
  # We always create the parent before the child, so we can rely on that here.
  for adgroup_criterion in result['value']:
    children[adgroup_criterion['criterion']['id']] = []

    if 'parentCriterionId' in adgroup_criterion['criterion']:
      children[adgroup_criterion['criterion']['parentCriterionId']].append(
          adgroup_criterion['criterion'])
    else:
      root_node = adgroup_criterion['criterion']

  # Show the tree
  DisplayTree(root_node, children)


def DisplayTree(node, children, level=0):
  """Recursively display a node and each of its children.

  Args:
    node: The node we're displaying the children of.
    children: Children of the parent node.
    level: How deep in the tree we are.
  """
  value = ''
  node_type = ''

  if 'caseValue' in node:
    case_value = node['caseValue']
    node_type = case_value['ProductDimension.Type']

    if node_type == 'ProductCanonicalCondition':
      value = (case_value['condition'] if 'condition' in case_value
               else 'OTHER')
    elif node_type == 'ProductBiddingCategory':
      value = '%s(%s)' % (case_value['type'], case_value['value']
                          if 'value' in case_value else 'OTHER')
    else:
      value = (case_value['value'] if 'value' in case_value else 'OTHER')

  print('%sid: %s, node_type: %s, value: %s\n'
        % (' ' * level, node['id'], node_type, value))

  for child_node in children[node['id']]:
    DisplayTree(child_node, children, level + 1)


if __name__ == '__main__':
  # Initialize client object.
  adwords_client = adwords.AdWordsClient.LoadFromStorage()

  main(adwords_client, ADGROUP_ID)

Set a product scope for a campaign

#!/usr/bin/env python
#
# Copyright 2016 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Restricts  products that will be included in a campaign with a ProductScope.

The LoadFromStorage method is pulling credentials and properties from a
"googleads.yaml" file. By default, it looks for this file in your home
directory. For more information, see the "Caching authentication information"
section of our README.

"""


# Import appropriate modules from the client library.
from googleads import adwords

CAMPAIGN_ID = 'INSERT_CAMPAIGN_ID_HERE'


def main(client, campaign_id):
  campaign_criterion_service = client.GetService(
      'CampaignCriterionService', version='v201809')

  product_scope = {
      'xsi_type': 'ProductScope',
      # This set of dimensions is for demonstration purposes only. It would be
      # extremely unlikely that you want to include so many dimensions in your
      # product scope.
      'dimensions': [
          {
              'xsi_type': 'ProductBrand',
              'value': 'Nexus'
          },
          {
              'xsi_type': 'ProductCanonicalCondition',
              'condition': 'NEW'
          },
          {
              'xsi_type': 'ProductCustomAttribute',
              'type': 'CUSTOM_ATTRIBUTE_0',
              'value': 'my attribute value'
          },
          {
              'xsi_type': 'ProductOfferId',
              'value': 'book1'
          },
          {
              'xsi_type': 'ProductType',
              'type': 'PRODUCT_TYPE_L1',
              'value': 'Media'
          },
          {
              'xsi_type': 'ProductType',
              'type': 'PRODUCT_TYPE_L2',
              'value': 'Books'
          },
          # The value for the bidding category is a fixed ID for the "Luggage
          # & Bags" category. You can retrieve IDs for categories from the
          # ConstantDataService. See the "GetProductCategoryTaxonomy" example
          # for more details.
          {
              'xsi_type': 'ProductBiddingCategory',
              'type': 'BIDDING_CATEGORY_L1',
              'value': '-5914235892932915235'
          }
      ]
  }

  campaign_criterion = {
      'campaignId': campaign_id,
      'criterion': product_scope
  }

  operations = [{
      'operator': 'ADD',
      'operand': campaign_criterion
  }]

  # Make the request
  result = campaign_criterion_service.mutate(operations)

  for criterion in result['value']:
    print('Created a ProductScope criterion with Id: %s'
          % criterion['criterion']['id'])


if __name__ == '__main__':
  # Initialize client object.
  adwords_client = adwords.AdWordsClient.LoadFromStorage()

  main(adwords_client, CAMPAIGN_ID)

Add a Shopping campaign

#!/usr/bin/env python
#
# Copyright 2016 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""This example adds a Shopping campaign.

The LoadFromStorage method is pulling credentials and properties from a
"googleads.yaml" file. By default, it looks for this file in your home
directory. For more information, see the "Caching authentication information"
section of our README.

"""


import uuid

# Import appropriate modules from the client library.
from googleads import adwords

BUDGET_ID = 'INSERT_BUDGET_ID_HERE'
# If set to true, a default partition will be created. If running the
# add_product_partition_tree.py example right after this example, make
# sure this stays set to false.
CREATE_DEFAULT_PARTITION = False
MERCHANT_ID = 'INSERT_MERCHANT_ID_HERE'


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)


def CreateDefaultPartition(client, ad_group_id):
  """Creates a default partition.

  Args:
    client: an AdWordsClient instance.
    ad_group_id: an integer ID for an ad group.
  """
  ad_group_criterion_service = client.GetService('AdGroupCriterionService',
                                                 version='v201809')

  operations = [{
      'operator': 'ADD',
      'operand': {
          'xsi_type': 'BiddableAdGroupCriterion',
          'adGroupId': ad_group_id,
          # Make sure that caseValue and parentCriterionId are left unspecified.
          # This makes this partition as generic as possible to use as a
          # fallback when others don't match.
          'criterion': {
              'xsi_type': 'ProductPartition',
              'partitionType': 'UNIT'
          },
          'biddingStrategyConfiguration': {
              'bids': [{
                  'xsi_type': 'CpcBid',
                  'bid': {
                      'microAmount': 500000
                  }
              }]
          }
      }
  }]

  ad_group_criterion = ad_group_criterion_service.mutate(operations)['value'][0]

  print('Ad group criterion with ID "%d" in ad group with ID "%d" was added.'
        % (ad_group_criterion['criterion']['id'],
            ad_group_criterion['adGroupId']))


if __name__ == '__main__':
  # Initialize client object.
  adwords_client = adwords.AdWordsClient.LoadFromStorage()

  main(adwords_client, BUDGET_ID, MERCHANT_ID, CREATE_DEFAULT_PARTITION)

Add a Smart Shopping campaign

#!/usr/bin/env python
#
# Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""This example adds a Smart Shopping campaign with an ad group and ad group ad.

The LoadFromStorage method is pulling credentials and properties from a
"googleads.yaml" file. By default, it looks for this file in your home
directory. For more information, see the "Caching authentication information"
section of our README.
"""


import uuid

# Import appropriate modules from the client library.
from googleads import adwords

# If set to true, a default partition will be created.
# Set it to false if you plan to run the add_product_partition_tree.py
# example right after this example.
CREATE_DEFAULT_PARTITION = False
MERCHANT_ID = 'INSERT_MERCHANT_ID_HERE'


def main(client, merchant_id, create_default_partition):
  budget_id = CreateBudget(client)
  campaign_id = CreateSmartCampaign(client, budget_id, merchant_id)
  ad_group_id = CreateSmartShoppingAdGroup(client, campaign_id)
  CreateSmartShoppingAd(client, ad_group_id)

  if create_default_partition:
    CreateDefaultPartition(client, ad_group_id)


def CreateBudget(client):
  """Adds a new budget.

  Args:
    client: an AdWordsClient instance.
  Returns:
    A budget ID.
  """
  budget_service = client.GetService('BudgetService', version='v201809')

  budget = {
      'name': 'Interplanetary budget #%s' % uuid.uuid4(),
      'amount': {
          'microAmount': '50000000'
      },
      'deliveryMethod': 'STANDARD',
      # Non-shared budgets are required for Smart Shopping campaigns.
      'isExplicitlyShared': False
  }

  budget_operations = [{
      'operator': 'ADD',
      'operand': budget
  }]

  # Add the budget.
  budget_id = budget_service.mutate(budget_operations)['value'][0][
      'budgetId']

  return budget_id


def CreateSmartCampaign(client, budget_id, merchant_id):
  """Adds a new Smart Shopping campaign.

  Args:
    client: an AdWordsClient instance.
    budget_id: the str ID of the budget to be associated with the Shopping
      campaign.
    merchant_id: the str ID of the merchant account to be associated with the
      Shopping campaign.
  Returns:
    A campaign ID.
  """
  campaign_service = client.GetService('CampaignService', version='v201809')
  # Create campaign with required and optional settings.
  campaign = {
      'name': 'Shopping campaign #%s' % uuid.uuid4(),
      # The advertisingChannelType is what makes this a Shopping campaign.
      'advertisingChannelType': 'SHOPPING',
      # Sets the advertisingChannelSubType to SHOPPING_GOAL_OPTIMIZED_ADS to
      # make this a Smart Shopping campaign.
      '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.
      'status': 'PAUSED',
      # Set portfolio budget (required).
      'budget': {'budgetId': budget_id},
      # Set a bidding strategy. Only MAXIMIZE_CONVERSION_VALUE is supported.
      'biddingStrategyConfiguration': {
          'biddingStrategyType': 'MAXIMIZE_CONVERSION_VALUE'
      },
      'settings': [{
          # All Shopping campaigns need a ShoppingSetting.
          'xsi_type': 'ShoppingSetting',
          'salesCountry': 'US',
          'merchantId': merchant_id
      }]
  }

  campaign_operations = [{
      'operator': 'ADD',
      'operand': campaign
  }]

  result = campaign_service.mutate(campaign_operations)['value'][0]

  print('Smart Shopping campaign with name "%s" and ID "%s" was added.'
        % (result['name'], result['id']))

  return result['id']


def CreateSmartShoppingAdGroup(client, campaign_id):
  """Adds a new Smart Shopping ad group.

  Args:
    client: an AdWordsClient instance.
    campaign_id: the str ID of a Smart Shopping campaign.
  Returns:
    An ad group ID.
  """
  ad_group_service = client.GetService('AdGroupService', version='v201809')
  # Create the ad group.
  ad_group = {
      'campaignId': campaign_id,
      'name': 'Smart Shopping ad group #%s' % uuid.uuid4(),
      # Set the ad group type to SHOPPING_GOAL_OPTIMIZED_ADS.
      'adGroupType': 'SHOPPING_GOAL_OPTIMIZED_ADS'
  }

  adgroup_operations = {
      'operator': 'ADD',
      'operand': ad_group
  }

  # Make the mutate request to add the AdGroup to the Smart 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))

  return ad_group_id


def CreateSmartShoppingAd(client, ad_group_id):
  """Adds a new Smart Shopping ad.

  Args:
    client: an AdWordsClient instance.
    ad_group_id: an integer ID for an ad group.
  """
  ad_group_ad_service = client.GetService('AdGroupAdService', version='v201809')
  # Create an AdGroup Ad.
  adgroup_ad = {
      'adGroupId': ad_group_id,
      # Create a Smart Shopping ad (Goal-optimized Shopping ad).
      'ad': {
          'xsi_type': 'GoalOptimizedShoppingAd'
      }
  }

  ad_operation = {
      'operator': 'ADD',
      'operand': adgroup_ad
  }

  # Make the mutate request to add the Smart Shopping ad to the AdGroup.
  ad_result = ad_group_ad_service.mutate([ad_operation])

  for adgroup_ad in ad_result['value']:
    print('Smart Shopping ad with ID "%s" was added.' % adgroup_ad['ad']['id'])


def CreateDefaultPartition(client, ad_group_id):
  """Creates a default partition.

  Args:
    client: an AdWordsClient instance.
    ad_group_id: an integer ID for an ad group.
  """
  ad_group_criterion_service = client.GetService('AdGroupCriterionService',
                                                 version='v201809')

  operations = [{
      'operator': 'ADD',
      'operand': {
          'xsi_type': 'BiddableAdGroupCriterion',
          'adGroupId': ad_group_id,
          # Make sure that caseValue and parentCriterionId are left unspecified.
          # This makes this partition as generic as possible to use as a
          # fallback when others don't match.
          'criterion': {
              'xsi_type': 'ProductPartition',
              'partitionType': 'UNIT'
          }
      }
  }]

  ad_group_criterion = ad_group_criterion_service.mutate(operations)['value'][0]

  print('Ad group criterion with ID "%d" in ad group with ID "%d" was added.'
        % (ad_group_criterion['criterion']['id'],
            ad_group_criterion['adGroupId']))


if __name__ == '__main__':
  # Initialize client object.
  adwords_client = adwords.AdWordsClient.LoadFromStorage()

  main(adwords_client, MERCHANT_ID, CREATE_DEFAULT_PARTITION)

Get the set of product bidding categories

#!/usr/bin/env python
#
# Copyright 2016 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""This example fetches the set of valid ProductBiddingCategories.

The LoadFromStorage method is pulling credentials and properties from a
"googleads.yaml" file. By default, it looks for this file in your home
directory. For more information, see the "Caching authentication information"
section of our README.

"""


# Import appropriate modules from the client library.
from googleads import adwords


def main(client):
  service = client.GetService('ConstantDataService', version='v201809')

  selector = {
      'predicates': [
          {
              'field': 'Country',
              'operator': 'IN',
              'values': ['US']
          }
      ]
  }

  result = service.getProductBiddingCategoryData(selector)

  bidding_categories = {}
  root_categories = []

  for product_bidding_category in result:
    category_id = product_bidding_category['dimensionValue']['value']
    parent_id = None
    name = product_bidding_category['displayValue'][0]['value']

    # Note: There may be cases where there isn't a value.
    if ('parentDimensionValue' in product_bidding_category and
        'value' in product_bidding_category['parentDimensionValue']):
      parent_id = product_bidding_category['parentDimensionValue']['value']

    if category_id not in bidding_categories:
      bidding_categories[category_id] = {}

    category = bidding_categories[category_id]

    if parent_id is not None:
      if parent_id not in bidding_categories:
        bidding_categories[parent_id] = {}

      parent = bidding_categories[parent_id]

      if 'children' not in parent:
        parent['children'] = []

      parent['children'].append(category)
    else:
      root_categories.append(category)

    category['id'] = category_id
    category['name'] = name

  DisplayCategories(root_categories)


def DisplayCategories(categories, prefix=''):
  for category in categories:
    print('%s%s [%s]' % (prefix, category['name'], category['id']))

    if 'children' in category:
      DisplayCategories(category['children'], '%s%s > ' % (prefix,
                                                           category['name']))


if __name__ == '__main__':
  # Initialize client object.
  adwords_client = adwords.AdWordsClient.LoadFromStorage()

  main(adwords_client)