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)