Shopping Campaign Samples

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

Add product partition tree

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright:: Copyright 2014, Google Inc. All Rights Reserved.
#
# License:: 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.

require 'adwords_api'

class ProductPartitionHelper
  attr_reader :operations

  def initialize(ad_group_id)
    # 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 criterionId 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.
    @next_id = -1

    # The set of mutate operations needed to create the current tree.
    @operations = []

    # The ID of the AdGroup that we wish to attach the partition tree to.
    @ad_group_id = ad_group_id
  end

  def create_subdivision(parent = nil, value = nil)
    division = {
      :xsi_type => 'ProductPartition',
      :partition_type => 'SUBDIVISION',
      :id => @next_id
    }

    @next_id -= 1

    unless parent.nil? || value.nil?
      division[:parent_criterion_id] = parent[:id]
      division[:case_value] = value
    end

    ad_group_criterion = {
      :xsi_type => 'BiddableAdGroupCriterion',
      :ad_group_id => @ad_group_id,
      :criterion => division
    }

    create_add_operation(ad_group_criterion)

    return division
  end

  def create_unit(parent = nil, value = nil, bid_amount = nil)
    unit = {
      :xsi_type => 'ProductPartition',
      :partition_type => 'UNIT'
    }

    unless parent.nil? || value.nil?
      unit[:parent_criterion_id] = parent[:id]
      unit[:case_value] = value
    end

    ad_group_criterion = {}
    if bid_amount && bid_amount > 0
      bidding_strategy_configuration = {
        :bids => [{
          :xsi_type => 'CpcBid',
          :bid => {
            :xsi_type => 'Money',
            :micro_amount => bid_amount
          }
        }]
      }
      ad_group_criterion[:xsi_type] = 'BiddableAdGroupCriterion'
      ad_group_criterion[:bidding_strategy_configuration] =
          bidding_strategy_configuration
    else
      ad_group_criterion[:xsi_type] = 'NegativeAdGroupCriterion'
    end
    ad_group_criterion[:ad_group_id] = @ad_group_id
    ad_group_criterion[:criterion] = unit

    create_add_operation(ad_group_criterion)

    return unit
  end

  private

  def create_add_operation(ad_group_criterion)
    operation = {
      :operator => 'ADD',
      :operand => ad_group_criterion
    }

    @operations << operation
  end
end

def display_tree(node, children, level = 0)
  value = ''
  type = ''

  if node[:case_value]
    type = node[:case_value][:product_dimension_type]

    value = case type
    when 'ProductCanonicalCondition'
      node[:case_value][:condition]
    when 'ProductBiddingCategory'
      "%s(%s)" % [node[:case_value][:type], node[:case_value][:value]]
    else
      node[:case_value][:value]
    end
  end

  puts "%sid: %s, type: %s, value: %s" %
      [' ' * level, node[:id], type, value]

  children[node[:id]].each do |child_node|
    display_tree(child_node, children, level + 1)
  end
end

def add_product_partition_tree(ad_group_id)
  # AdwordsApi::Api will read a config file from ENV['HOME']/adwords_api.yml
  # when called without parameters.
  adwords = AdwordsApi::Api.new

  # To enable logging of SOAP requests, set the log_level value to 'DEBUG' in
  # the configuration file or provide your own logger:
  # adwords.logger = Logger.new('adwords_xml.log')

  ad_group_criterion_srv =
      adwords.service(:AdGroupCriterionService, API_VERSION)

  helper = ProductPartitionHelper.new(ad_group_id)

  root = helper.create_subdivision()

  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.create_unit(root, new_product_canonical_condition, 200000)
  helper.create_unit(root, used_product_canonical_condition, 100000)
  other_condition =
      helper.create_subdivision(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.create_unit(other_condition, cool_product_brand, 900000)
  helper.create_unit(other_condition, cheap_product_brand, 10000)
  other_brand = helper.create_subdivision(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 get_product_taxonomy 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.create_unit(other_brand, luggage_category, 750000)
  helper.create_unit(other_brand, generic_category, 110000)

  # Make the mutate request.
  result = ad_group_criterion_srv.mutate(helper.operations)

  children = {}
  root_node = nil
  # 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.
  result[:value].each do |criterion|
    children[criterion[:criterion][:id]] = []

    if criterion[:criterion][:parent_criterion_id]
      children[criterion[:criterion][:parent_criterion_id]] <<
          criterion[:criterion]
    else
      root_node = criterion[:criterion]
    end
  end

  display_tree(root_node, children)
end

if __FILE__ == $0
  API_VERSION = :v201710

  begin
    ad_group_id = 'INSERT_AD_GROUP_ID_HERE'.to_i

    add_product_partition_tree(ad_group_id)

  # Authorization error.
  rescue AdsCommon::Errors::OAuth2VerificationRequired => e
    puts "Authorization credentials are not valid. Edit adwords_api.yml for " +
        "OAuth2 client ID and secret and run misc/setup_oauth2.rb example " +
        "to retrieve and store OAuth2 tokens."
    puts "See this wiki page for more details:\n\n  " +
        'https://github.com/googleads/google-api-ads-ruby/wiki/OAuth2'

  # HTTP errors.
  rescue AdsCommon::Errors::HttpError => e
    puts "HTTP Error: %s" % e

  # API errors.
  rescue AdwordsApi::Errors::ApiException => e
    puts "Message: %s" % e.message
    puts 'Errors:'
    e.errors.each_with_index do |error, index|
      puts "\tError [%d]:" % (index + 1)
      error.each do |field, value|
        puts "\t\t%s: %s" % [field, value]
      end
    end
  end
end

Add product scope

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright:: Copyright 2014, Google Inc. All Rights Reserved.
#
# License:: 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 restricts the products that will be included in the campaign by
# setting a ProductScope.

require 'adwords_api'

def add_product_scope(campaign_id)
  # AdwordsApi::Api will read a config file from ENV['HOME']/adwords_api.yml
  # when called without parameters.
  adwords = AdwordsApi::Api.new

  # To enable logging of SOAP requests, set the log_level value to 'DEBUG' in
  # the configuration file or provide your own logger:
  # adwords.logger = Logger.new('adwords_xml.log')

  campaign_criterion_srv = adwords.service(:CampaignCriterionService,
      API_VERSION)

  product_scope = {
    # This set of dimensions is for demonstration purposes only. It is
    # extremely unlikely that you want to include so many dimensions in your
    # product scope.
    :xsi_type => 'ProductScope',
    :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 'get_product_category_taxonomy'
      # example for more details.
      {
        :xsi_type => 'ProductBiddingCategory',
        :type => 'BIDDING_CATEGORY_L1',
        :value => '-5914235892932915235',
      }
    ]
  }

  campaign_criterion = {
    :campaign_id => campaign_id,
    :criterion => product_scope
  }

  campaign_criterion_operation = {
    :operator => 'ADD',
    :operand => campaign_criterion
  }

  # Make the mutate request.
  result = campaign_criterion_srv.mutate([campaign_criterion_operation])

  campaign_criterion = result[:value].first
  puts "Created a ProductScope criterion with ID %d." %
      [campaign_criterion[:criterion][:id]]
end

if __FILE__ == $0
  API_VERSION = :v201710

  begin
    campaign_id = 'INSERT_CAMPAIGN_ID_HERE'.to_i

    add_product_scope(campaign_id)

  # Authorization error.
  rescue AdsCommon::Errors::OAuth2VerificationRequired => e
    puts "Authorization credentials are not valid. Edit adwords_api.yml for " +
        "OAuth2 client ID and secret and run misc/setup_oauth2.rb example " +
        "to retrieve and store OAuth2 tokens."
    puts "See this wiki page for more details:\n\n  " +
        'https://github.com/googleads/google-api-ads-ruby/wiki/OAuth2'

  # HTTP errors.
  rescue AdsCommon::Errors::HttpError => e
    puts "HTTP Error: %s" % e

  # API errors.
  rescue AdwordsApi::Errors::ApiException => e
    puts "Message: %s" % e.message
    puts 'Errors:'
    e.errors.each_with_index do |error, index|
      puts "\tError [%d]:" % (index + 1)
      error.each do |field, value|
        puts "\t\t%s: %s" % [field, value]
      end
    end
  end
end

Add Shopping campaign

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright:: Copyright 2014, Google Inc. All Rights Reserved.
#
# License:: 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.

require 'adwords_api'
require 'date'

def add_shopping_campaign(budget_id, merchant_id, create_default_partition)
  # AdwordsApi::Api will read a config file from ENV['HOME']/adwords_api.yml
  # when called without parameters.
  adwords = AdwordsApi::Api.new

  # To enable logging of SOAP requests, set the log_level value to 'DEBUG' in
  # the configuration file or provide your own logger:
  # adwords.logger = Logger.new('adwords_xml.log')

  campaign_srv = adwords.service(:CampaignService, API_VERSION)
  ad_group_srv = adwords.service(:AdGroupService, API_VERSION)
  ad_group_ad_srv = adwords.service(:AdGroupAdService, API_VERSION)

  # 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])

  # Print the result.
  campaign = result[:value].first
  puts "Campaign with name '%s' and ID %d was added." %
      [campaign[:name], campaign[:id]]

  # Create ad group.
  ad_group = {
    :campaign_id => campaign[:id],
    :name => 'Ad Group #%d' % (Time.new.to_f * 1000).to_i
  }
  ad_group_operation = {:operator => 'ADD', :operand => ad_group}

  # Make the mutate request.
  result = ad_group_srv.mutate([ad_group_operation])

  # Print the result.
  ad_group = result[:value].first
  puts "Ad group with name '%s' and ID %d was added." %
      [ad_group[:name], ad_group[:id]]

  # Create product ad.
  ad_group_ad = {
    :ad_group_id => ad_group[:id],
    :ad => {:xsi_type => 'ProductAd'}
  }
  ad_group_operation = {:operator => 'ADD', :operand => ad_group_ad}

  # Make the mutate request.
  result = ad_group_ad_srv.mutate([ad_group_operation])

  # Print the result.
  ad_group_ad = result[:value].first
  puts "Product ad with ID %d was added." % [ad_group_ad[:id]]

  if create_default_partition
    ad_group_criterion_srv =
        adwords.service(:AdGroupCriterionService, API_VERSION)

    product_partition = {
      :partition_type => 'UNIT',
      # Make sure that caseValue and parentCriterionId are nil. This
      # makes this partition as generic as possible to use as a fallback
      # when others don't match.
      :case_value => nil,
      :parent_criterion_id => nil
    }

    ad_group_criterion = {
      :ad_group_id => ad_group[:id],
      :criterion => product_partition,
      :bidding_strategy_configuration => {
        :bids => [{
          :xsi_type => 'CpcBid',
          :bid => {:micro_amount => 500_000}
        }]
      }
    }

    operation = {
      :operator => 'ADD',
      :operand => ad_group_criterion
    }

    result = ad_group_criterion_srv.mutate([operation])

    ad_group_criterion = result[:value].first
    puts "Ad group criterion with ID %d in ad group with ID %d was added." %
        [ad_group_criterion[:criterion][:id], ad_group_criterion[:ad_group_id]]
  end
end

if __FILE__ == $0
  API_VERSION = :v201710

  begin
    budget_id = 'INSERT_BUDGET_ID_HERE'.to_i
    merchant_id = 'INSERT_MERCHANT_ID_HERE'.to_i

    # If set to true, a default partition will be created. If running the
    # add_product_partition_tree.rb example right after this example, make
    # sure this stays set to false.
    create_default_partition = false

    add_shopping_campaign(budget_id, merchant_id)

  # Authorization error.
  rescue AdsCommon::Errors::OAuth2VerificationRequired => e
    puts "Authorization credentials are not valid. Edit adwords_api.yml for " +
        "OAuth2 client ID and secret and run misc/setup_oauth2.rb example " +
        "to retrieve and store OAuth2 tokens."
    puts "See this wiki page for more details:\n\n  " +
        'https://github.com/googleads/google-api-ads-ruby/wiki/OAuth2'

  # HTTP errors.
  rescue AdsCommon::Errors::HttpError => e
    puts "HTTP Error: %s" % e

  # API errors.
  rescue AdwordsApi::Errors::ApiException => e
    puts "Message: %s" % e.message
    puts 'Errors:'
    e.errors.each_with_index do |error, index|
      puts "\tError [%d]:" % (index + 1)
      error.each do |field, value|
        puts "\t\t%s: %s" % [field, value]
      end
    end
  end
end

Add campaign for Showcase Shopping ads

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright:: Copyright 2017, Google Inc. All Rights Reserved.
#
# License:: 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 for Showcase ads.

require 'adwords_api'
require 'date'
require 'base64'

def add_shopping_campaign_for_showcase_ads(budget_id, merchant_id)
  # AdwordsApi::Api will read a config file from ENV['HOME']/adwords_api.yml
  # when called without parameters.
  adwords = AdwordsApi::Api.new

  # To enable logging of SOAP requests, set the log_level value to 'DEBUG' in
  # the configuration file or provide your own logger:
  # adwords.logger = Logger.new('adwords_xml.log')

  campaign = create_campaign(adwords, budget_id, merchant_id)
  puts "Campaign with name '%s' and ID %d was added." %
      [campaign[:name], campaign[:id]]

  ad_group = create_ad_group(adwords, campaign)
  puts "Ad group with name '%s' and ID %d was added." %
      [ad_group[:name], ad_group[:id]]

  ad_group_ad = create_showcase_ad(adwords, ad_group)
  puts "Showcase ad with ID %d was added." % ad_group_ad[:ad][:id]

  ad_group_criterion = create_product_partitions(adwords, ad_group[:id])
  puts "Product partition tree with %d nodes was added." %
      ad_group_criterion.length
end

def create_campaign(adwords, budget_id, merchant_id)
  campaign_srv = adwords.service(:CampaignService, API_VERSION)

  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])
  campaign = result[:value].first
  return campaign
end

def create_ad_group(adwords, campaign)
  ad_group_srv = adwords.service(:AdGroupService, API_VERSION)

  ad_group = {
    :campaign_id => campaign[:id],
    :name => 'Ad Group #%d' % (Time.new.to_f * 1000).to_i,
    # Required: Set the ad group type to SHOPPING_SHOWCASE_ADS.
    :ad_group_type => 'SHOPPING_SHOWCASE_ADS',
    # Required: Set the ad group's bidding strategy configuration.
    # Showcase ads require either ManualCpc or EnhancedCpc in the campaign's
    # bidding strategy configuration.
    :bidding_strategy_configuration => {
      :bids => [
        :xsi_type => 'CpcBid',
        :bid => {:micro_amount => 1000000}
      ]
    }
  }

  ad_group_operation = {:operator => 'ADD', :operand => ad_group}

  # Make the mutate request.
  result = ad_group_srv.mutate([ad_group_operation])
  ad_group = result[:value].first
  return ad_group
end

def create_showcase_ad(adwords, ad_group)
  ad_group_ad_srv = adwords.service(:AdGroupAdService, API_VERSION)

  ad_group_ad = {
    :ad_group_id => ad_group[:id],
    :ad => {
      :xsi_type => 'ShowcaseAd',
      :name => 'Showcase ad #%d' % (Time.new.to_f * 1000).to_i,
      :final_urls => ['http://example.com/showcase'],
      :display_url => 'example.com',
      # Required: Set the ad's expanded image.
      :expanded_image => {
        :media_id => upload_image(adwords, 'https://goo.gl/IfVlpF')
      },
      # Optional: Set the collapsed image.
      :collapsed_image => {
        :media_id => upload_image(adwords, 'https://goo.gl/NqTxAE')
      }
    }
  }

  ad_group_operation = {:operator => 'ADD', :operand => ad_group_ad}

  # Make the mutate request.
  result = ad_group_ad_srv.mutate([ad_group_operation])
  ad_group_ad = result[:value].first
  return ad_group_ad
end

def upload_image(adwords, image_url)
  media_srv = adwords.service(:MediaService, API_VERSION)

  raw_image_data = AdsCommon::Http.get(image_url, adwords.config)
  image = {
    :xsi_type => 'Image',
    :data => Base64.encode64(raw_image_data),
    :type => 'IMAGE'
  }

  # Upload the image.
  response = media_srv.upload([image])
  image = response.first
  return image[:media_id]
end

def create_product_partitions(adwords, ad_group_id)
  ad_group_criterion_srv = adwords.service(:AdGroupCriterionService,
      API_VERSION)

  helper = ProductPartitionHelper.new(ad_group_id)

  root = helper.create_subdivision()

  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.create_unit(root, new_product_canonical_condition)
  helper.create_unit(root, used_product_canonical_condition)
  helper.create_unit(root, other_product_canonical_condition)

  result = ad_group_criterion_srv.mutate(helper.operations)
  ad_group_criterion = result[:value]
  return ad_group_criterion
end

class ProductPartitionHelper
  attr_reader :operations

  def initialize(ad_group_id)
    # 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 criterionId 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.
    @next_id = -1

    # The set of mutate operations needed to create the current tree.
    @operations = []

    # The ID of the AdGroup that we wish to attach the partition tree to.
    @ad_group_id = ad_group_id
  end

  def create_subdivision(parent = nil, value = nil)
    division = {
      :xsi_type => 'ProductPartition',
      :partition_type => 'SUBDIVISION',
      :id => @next_id
    }

    @next_id -= 1

    unless parent.nil? || value.nil?
      division[:parent_criterion_id] = parent[:id]
      division[:case_value] = value
    end

    ad_group_criterion = {
      :xsi_type => 'BiddableAdGroupCriterion',
      :ad_group_id => @ad_group_id,
      :criterion => division
    }

    create_add_operation(ad_group_criterion)

    return division
  end

  def create_unit(parent = nil, value = nil, bid_amount = nil)
    unit = {
      :xsi_type => 'ProductPartition',
      :partition_type => 'UNIT'
    }

    unless parent.nil? || value.nil?
      unit[:parent_criterion_id] = parent[:id]
      unit[:case_value] = value
    end

    ad_group_criterion = {}
    if bid_amount && bid_amount > 0
      bidding_strategy_configuration = {
        :bids => [{
          :xsi_type => 'CpcBid',
          :bid => {
            :xsi_type => 'Money',
            :micro_amount => bid_amount
          }
        }]
      }
      ad_group_criterion[:xsi_type] = 'BiddableAdGroupCriterion'
      ad_group_criterion[:bidding_strategy_configuration] =
          bidding_strategy_configuration
    else
      ad_group_criterion[:xsi_type] = 'NegativeAdGroupCriterion'
    end
    ad_group_criterion[:ad_group_id] = @ad_group_id
    ad_group_criterion[:criterion] = unit

    create_add_operation(ad_group_criterion)

    return unit
  end

  private

  def create_add_operation(ad_group_criterion)
    operation = {
      :operator => 'ADD',
      :operand => ad_group_criterion
    }

    @operations << operation
  end
end

if __FILE__ == $0
  API_VERSION = :v201710

  begin
    budget_id = 'INSERT_BUDGET_ID_HERE'.to_i
    merchant_id = 'INSERT_MERCHANT_ID_HERE'.to_i

    add_shopping_campaign_for_showcase_ads(budget_id, merchant_id)

  # Authorization error.
  rescue AdsCommon::Errors::OAuth2VerificationRequired => e
    puts "Authorization credentials are not valid. Edit adwords_api.yml for " +
        "OAuth2 client ID and secret and run misc/setup_oauth2.rb example " +
        "to retrieve and store OAuth2 tokens."
    puts "See this wiki page for more details:\n\n  " +
        'https://github.com/googleads/google-api-ads-ruby/wiki/OAuth2'

  # HTTP errors.
  rescue AdsCommon::Errors::HttpError => e
    puts "HTTP Error: %s" % e

  # API errors.
  rescue AdwordsApi::Errors::ApiException => e
    puts "Message: %s" % e.message
    puts 'Errors:'
    e.errors.each_with_index do |error, index|
      puts "\tError [%d]:" % (index + 1)
      error.each do |field, value|
        puts "\t\t%s: %s" % [field, value]
      end
    end
  end
end

Get product category taxonomy

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright:: Copyright 2014, Google Inc. All Rights Reserved.
#
# License:: 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.

require 'adwords_api'

def display_categories(categories, prefix='')
  categories.each do |category|
    puts "%s%s [%s]" % [prefix, category[:name], category[:id]]
    if category[:children]
      display_categories(category[:children],
          "%s%s > " % [prefix, category[:name]])
    end
  end
end

def get_product_category_taxonomy()
  # AdwordsApi::Api will read a config file from ENV['HOME']/adwords_api.yml
  # when called without parameters.
  adwords = AdwordsApi::Api.new

  # To enable logging of SOAP requests, set the log_level value to 'DEBUG' in
  # the configuration file or provide your own logger:
  # adwords.logger = Logger.new('adwords_xml.log')

  constant_data_srv = adwords.service(:ConstantDataService, API_VERSION)

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

  result = constant_data_srv.get_product_bidding_category_data(selector)

  bidding_categories = {}
  root_categories = []

  result.each do |product_bidding_category|
    id = product_bidding_category[:dimension_value][:value]
    parent_id = nil
    name = product_bidding_category[:display_value].values.first()

    if product_bidding_category[:parent_dimension_value]
      parent_id = product_bidding_category[:parent_dimension_value][:value]
    end

    bidding_categories[id] ||= {}

    category = bidding_categories[id]

    if parent_id
      bidding_categories[parent_id] ||= {}

      parent = bidding_categories[parent_id]

      parent[:children] ||= []
      parent[:children] << category
    else
      root_categories << category
    end

    category[:id] = id
    category[:name] = name
  end

  display_categories(root_categories)
end

if __FILE__ == $0
  API_VERSION = :v201710

  begin
    get_product_category_taxonomy()

  # Authorization error.
  rescue AdsCommon::Errors::OAuth2VerificationRequired => e
    puts "Authorization credentials are not valid. Edit adwords_api.yml for " +
        "OAuth2 client ID and secret and run misc/setup_oauth2.rb example " +
        "to retrieve and store OAuth2 tokens."
    puts "See this wiki page for more details:\n\n  " +
        'https://github.com/googleads/google-api-ads-ruby/wiki/OAuth2'

  # HTTP errors.
  rescue AdsCommon::Errors::HttpError => e
    puts "HTTP Error: %s" % e

  # API errors.
  rescue AdwordsApi::Errors::ApiException => e
    puts "Message: %s" % e.message
    puts 'Errors:'
    e.errors.each_with_index do |error, index|
      puts "\tError [%d]:" % (index + 1)
      error.each do |field, value|
        puts "\t\t%s: %s" % [field, value]
      end
    end
  end
end

Send feedback about...

AdWords API
AdWords API
Need help? Visit our support page.