If you're still on v201609, please migrate to a newer version before October 2, 2017.

Advanced Operations Samples

The code samples below provide examples of common advanced operations using the AdWords API. Client Library.

Add an ad customizer

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright:: Copyright 2015, 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 an ad customizer feed using Extension Services. Then it adds
# an ad that uses the feed to populate dynamic data.

require 'adwords_api'
require 'date'

def add_ad_customizers(feed_name, ad_group_ids)
  # 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')

  feed_item_srv = adwords.service(:FeedItemService, API_VERSION)
  ad_group_ad_srv = adwords.service(:AdGroupAdService, API_VERSION)

  # Create a customizer feed. One feed per account can be used for all ads.
  feed_data = create_customizer_feed(adwords, feed_name)

  # Now adding feed items -- the values we'd like to place.
  now_date = Date.today()

  items_data = [
    {
      :name => 'Mars',
      :price => '$1234.56',
      :date => now_date.strftime('%Y%m01 000000'),
      :ad_group_id => ad_group_ids[0]
    },
    {
      :name => 'Venus',
      :price => '$1450.00',
      :date => now_date.strftime('%Y%m15 000000'),
      :ad_group_id => ad_group_ids[1]
     }
  ]

  feed_items = items_data.map do |item|
    {
      :feed_id => feed_data[:feed_id],
      :attribute_values => [
        {
          :feed_attribute_id => feed_data[:name_id],
          :string_value => item[:name]
        },
        {
          :feed_attribute_id => feed_data[:price_id],
          :string_value => item[:price]
        },
        {
          :feed_attribute_id => feed_data[:date_id],
          :string_value => item[:date]
        }
      ],
      :ad_group_targeting => {
        :targeting_ad_group_id => item[:ad_group_id]
      }
    }
  end

  feed_items_operations = feed_items.map do |item|
    {:operator => 'ADD', :operand => item}
  end

  response = feed_item_srv.mutate(feed_items_operations)
  if response and response[:value]
    response[:value].each do |feed_item|
      puts 'Feed item with ID %d was added.' % feed_item[:feed_item_id]
    end
  else
    raise new StandardError, 'No feed items were added.'
  end

  # All set! We can now create ads with customizations.
  expanded_text_ad = {
    :xsi_type => 'ExpandedTextAd',
    :headline_part1 => 'Luxury Cruise to {=%s.Name}' % feed_name,
    :headline_part2 => 'Only {=%s.Price}' % feed_name,
    :description => 'Offer ends in {=countdown(%s.Date)}!' % feed_name,
    :final_urls => ['http://www.example.com']
  }

  # We add the same ad to both ad groups. When they serve, they will show
  # different values, since they match different feed items.
  operations = ad_group_ids.map do |ad_group_id|
    {
      :operator => 'ADD',
      :operand => {
        :ad_group_id => ad_group_id,
        :ad => expanded_text_ad.dup()
      }
    }
  end

  response = ad_group_ad_srv.mutate(operations)
  if response and response[:value]
    ads = response[:value]
    ads.each do |ad|
      puts "\tCreated an ad with ID %d, type '%s' and status '%s'" %
          [ad[:ad][:id], ad[:ad][:ad_type], ad[:status]]
    end
  else
    raise StandardError, 'No ads were added.'
  end
end

def create_customizer_feed(adwords, feed_name)
  ad_customizer_srv = adwords.service(:AdCustomizerFeedService, API_VERSION)
  feed = {
    :feed_name => feed_name,
    :feed_attributes => [
      {:name => 'Name', :type => 'STRING'},
      {:name => 'Price', :type => 'PRICE'},
      {:name => 'Date', :type => 'DATE_TIME'}
    ]
  }
  operation = {:operand => feed, :operator => 'ADD'}
  added_feed = ad_customizer_srv.mutate([operation])[:value].first()
  puts "Created ad customizer feed with ID = %d and name = '%s'." %
      [added_feed[:feed_id], added_feed[:feed_name]]
  return {
    :feed_id => added_feed[:feed_id],
    :name_id => added_feed[:feed_attributes][0][:id],
    :price_id => added_feed[:feed_attributes][1][:id],
    :date_id => added_feed[:feed_attributes][2][:id]
  }
end

if __FILE__ == $0
  API_VERSION = :v201708

  begin
    feed_name = 'INSERT_FEED_NAME_HERE'.to_s
    ad_group_ids = [
        'INSERT_AD_GROUP_ID_HERE'.to_i,
        'INSERT_AD_GROUP_ID_HERE'.to_i
    ]
    add_ad_customizers(feed_name, ad_group_ids)

  # 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 an ad group bid modifier

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright:: Copyright 2013, 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 illustrates how to add an ad group level mobile bid modifier
# override for a campaign.

require 'adwords_api'

def add_ad_group_bid_modifier(ad_group_id, bid_modifier)
  # 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')

  bid_modifier_srv = adwords.service(:AdGroupBidModifierService, API_VERSION)

  # Mobile criterion ID.
  criterion_id = 30001

  # Prepare to add an ad group level override.
  operation = {
    # Use 'ADD' to add a new modifier and 'SET' to update an existing one. A
    # modifier can be removed with the 'REMOVE' operator.
    :operator => 'ADD',
    :operand => {
      :ad_group_id => ad_group_id,
      :criterion => {
        :xsi_type => 'Platform',
        :id => criterion_id
      },
      :bid_modifier => bid_modifier
    }
  }

  # Add ad group level mobile bid modifier.
  response = bid_modifier_srv.mutate([operation])
  if response and response[:value]
    modifier = response[:value].first
    value = modifier[:bid_modifier] || 'unset'
    puts ('Campaign ID %d, AdGroup ID %d, Criterion ID %d was updated with ' +
        'ad group level modifier: %s') %
           [modifier[:campaign_id], modifier[:ad_group_id],
            modifier[:criterion][:id], value]
  else
    puts 'No modifiers were added.'
  end
end

if __FILE__ == $0
  API_VERSION = :v201708

  begin
    # ID of an ad group to add an override for.
    ad_group_id = 'INSERT_AD_GROUP_ID_HERE'.to_i
    # Bid modifier to override with.
    bid_modifier = 1.5

    add_ad_group_bid_modifier(ad_group_id, bid_modifier)

  # 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 a click-to-download ad

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright:: Copyright 2013, 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 code example creates a click-to-download ad, also known as an app
# promotion ad to a given ad group. To list ad groups, run get_ad_groups.rb.

require 'adwords_api'
require 'base64'

def add_click_to_download_ad(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_ad_srv = adwords.service(:AdGroupAdService, API_VERSION)

  # Optionally specify a landscape image. The image needs to be in a BASE64
  # encoded form. Here we download a demo image and encode it for this ad.
  image_url = 'http://goo.gl/9JmyKk'
  image_data = AdsCommon::Http.get(image_url, adwords.config)
  image_data_base64 = Base64.encode64(image_data)

  # Create the template elements for the ad. You can refer to
  #     https://developers.google.com/adwords/api/docs/appendix/templateads
  # for the list of available template fields.
  ad_data = {
    :unique_name => 'adData',
    :fields => [
      {
        :name => 'headline',
        :field_text => 'Enjoy your drive in Mars',
        :type => 'TEXT'
      },
      {
        :name => 'description1',
        :field_text => 'Realistic physics simulation',
        :type => 'TEXT'
      },
      {
        :name => 'description2',
        :field_text => 'Race against players online',
        :type => 'TEXT'
      },
      {
        :name => 'appId',
        :field_text => 'com.example.demogame',
        :type => 'TEXT'
      },
      {
        :name => 'appStore',
        :field_text => '2',
        :type => 'ENUM'
      },
      {
        :name => 'landscapeImage',
        :field_media => {
          :xsi_type => 'Image',
          :data => image_data_base64,
        },
        :type => 'IMAGE'
      }
    ]
  }

  # Create click to download ad.
  click_to_download_app_ad = {
    :xsi_type => 'TemplateAd',
    :name => 'Ad for demo game',
    :template_id => 353,
    :final_urls =>
        ['http://play.google.com/store/apps/details?id=com.example.demogame'],
    :display_url => 'play.google.com',
    :template_elements => [ad_data]
  }

  # Create ad group ad.
  ad_group_ad = {
    :ad_group_id => ad_group_id,
    :ad => click_to_download_app_ad,
    # Optional.
    :status => 'PAUSED'
  }

  # Add ad.
  response = ad_group_ad_srv.mutate([
      {:operator => 'ADD', :operand => ad_group_ad}
  ])
  if response and response[:value]
    response[:value].each do |ad|
      puts 'Added new click-to-download ad with ID %d and final url "%s".' %
          [ad[:ad][:id], ad[:ad][:final_urls][0]]
    end
  else
    raise StandardError, 'No ads were added.'
  end
end

if __FILE__ == $0
  API_VERSION = :v201708

  begin
    # Ad group ID to add text ads to.
    ad_group_id = 'INSERT_AD_GROUP_ID_HERE'.to_i
    add_click_to_download_ad(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 a page feed specifying URLs for a DSA campaign

#!/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 code example adds a page feed to specify precisely which URLs to use with
# your Dynamic Search Ads campaign. To use a Dynamic Search Ads campaign, run
# add_dynamic_search_ads_campaign.rb. To get campaigns, run get_campaigns.rb.

require 'adwords_api'

def add_dynamic_page_feed(campaign_id, 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')

  dsa_page_url_label = "discounts"

  # Get the page feed details. This code example creates a new feed, but you can
  # fetch and re-use an existing feed.
  feed_details = create_feed(adwords)
  create_feed_mapping(adwords, feed_details)
  create_feed_items(adwords, feed_details, dsa_page_url_label)

  # Associate the page feed with the campaign.
  update_campaign_dsa_setting(adwords, campaign_id, feed_details)

  # Optional: Target web pages matching the feed's label in the ad group.
  add_dsa_targeting(adwords, ad_group_id, dsa_page_url_label)

  puts "Dynamic page feed setup is complete for campaign ID %d." % campaign_id
end

def create_feed(adwords)
  feed_srv = adwords.service(:FeedService, API_VERSION)

  url_attribute = {
    :type => 'URL_LIST',
    :name => 'Page URL'
  }

  label_attribute = {
    :type => 'STRING_LIST',
    :name => 'Label'
  }

  dsa_page_feed = {
    :name => 'DSA Feed #%s' % (Time.now.to_f * 1000).to_i,
    :attributes => [url_attribute, label_attribute],
    :origin => 'USER'
  }

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

  new_feed = feed_srv.mutate([operation])[:value].first

  feed_details = {
    :feed_id => new_feed[:id],
    :url_attribute_id => new_feed[:attributes][0][:id],
    :label_attribute_id => new_feed[:attributes][1][:id]
  }
  puts ("Feed with name '%s' and ID %d with urlAttributeId %d and " +
      "labelAttributeId %d was created.") % [
        new_feed[:name],
        feed_details[:feed_id],
        feed_details[:url_attribute_id],
        feed_details[:label_attribute_id]
      ]

  return feed_details
end

def create_feed_mapping(adwords, feed_details)
  feed_mapping_srv = adwords.service(:FeedMappingService, API_VERSION)

  url_field_mapping = {
    :feed_attribute_id => feed_details[:url_attribute_id],
    :field_id => DSA_PAGE_URLS_FIELD_ID
  }

  label_field_mapping = {
    :feed_attribute_id => feed_details[:label_attribute_id],
    :field_id => DSA_LABEL_FIELD_ID
  }

  feed_mapping = {
    :criterion_type => DSA_PAGE_FEED_CRITERION_TYPE,
    :feed_id => feed_details[:feed_id],
    :attribute_field_mappings => [url_field_mapping, label_field_mapping]
  }

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

  new_feed_mapping = feed_mapping_srv.mutate([operation])[:value].first
  puts ("Feed mapping with ID %d and criterionType %d was saved for feed " +
      "with ID %d.") % [
        new_feed_mapping[:feed_mapping_id],
        new_feed_mapping[:criterion_type],
        new_feed_mapping[:feed_id]
      ]
end

def create_feed_items(adwords, feed_details, label_name)
  feed_item_srv = adwords.service(:FeedItemService, API_VERSION)

  operations = [
    create_dsa_url_add_operation(
      feed_details,
      'http://www.example.com/discounts/rental-cars',
      label_name
    ),
    create_dsa_url_add_operation(
      feed_details,
      'http://www.example.com/discounts/hotel-deals',
      label_name
    ),
    create_dsa_url_add_operation(
      feed_details,
      'http://www.example.com/discounts/flight-deals',
      label_name
    )
  ]

  result = feed_item_srv.mutate(operations)
  result[:value].each do |item|
    puts "Feed item with feed item ID %d was added." % item[:feed_item_id]
  end
end

def create_dsa_url_add_operation(feed_details, url, label_name)
  # Optional: Add the {feeditem} valuetrack parameter to track which page
  # feed items lead to each click.
  url = url + '?id={feeditem}'

  url_attribute_value = {
    :feed_attribute_id => feed_details[:url_attribute_id],
    :string_values => [url]
  }

  label_attribute_value = {
    :feed_attribute_id => feed_details[:label_attribute_id],
    :string_values => [label_name]
  }

  feed_item = {
    :feed_id => feed_details[:feed_id],
    :attribute_values => [url_attribute_value, label_attribute_value]
  }

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

  return operation
end

def update_campaign_dsa_setting(adwords, campaign_id, feed_details)
  campaign_srv = adwords.service(:CampaignService, API_VERSION)

  selector = {
    :fields => ['Id', 'Settings'],
    :predicates => [
      {:field => 'CampaignId', :operator => 'IN', :values => [campaign_id]}
    ]
  }

  campaign_page = campaign_srv.get(selector)
  if campaign_page.nil? or campaign_page[:entries].nil? or
      campaign_page[:total_num_entries] == 0
    raise 'No campaign found with ID: %d' % campaign_id
  end

  campaign = campaign_page[:entries].first

  if campaign[:settings].nil?
    raise 'Campaign with ID %d is not a DSA campaign.' % campaign_id
  end

  dsa_setting = nil
  campaign[:settings].each do |setting|
    if setting[:setting_type] == 'DynamicSearchAdsSetting'
      dsa_setting = setting
      break
    end
  end

  if dsa_setting.nil?
    raise 'Campaign with ID %d is not a DSA campaign.' % campaign_id
  end

  # Use a page feed to specify precisely which URLs to use with your Dynamic
  # Search Ads.
  page_feed = {
    :feed_ids => [feed_details[:feed_id]]
  }
  dsa_setting[:page_feed] = page_feed

  # Optional: Specify whether only the supplied URLs should be used with your
  # Dynamic Search Ads.
  dsa_setting[:use_supplied_urls_only] = true

  updated_campaign = {
    :id => campaign_id,
    :settings => campaign[:settings]
  }

  operation = {
    :operand => updated_campaign,
    :operator => 'SET'
  }

  updated_campaign = campaign_srv.mutate([operation])[:value].first
  puts "DSA page feed for campaign ID %d was updated with feed ID %d." % [
    updated_campaign[:id], feed_details[:feed_id]
  ]
end

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

  webpage = {
    :xsi_type => 'Webpage',
    :parameter => {
      :criterion_name => 'Test criterion',
      :conditions => [{
        :operand => 'CUSTOM_LABEL',
        :argument => dsa_page_url_label
      }]
    }
  }

  bidding_strategy_configuration = {
    :bids => [{
      :xsi_type => 'CpcBid',
      :bid => {
        :micro_amount => 1_500_000
      }
    }]
  }

  criterion = {
    :xsi_type => 'BiddableAdGroupCriterion',
    :ad_group_id => ad_group_id,
    :criterion => webpage,
    :bidding_strategy_configuration => bidding_strategy_configuration
  }

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

  new_criterion = ad_group_criterion_srv.mutate([operation])[:value].first
  puts "Web page criterion with ID %d and status '%s' was created." %
      [new_criterion[:criterion][:id], new_criterion[:user_status]]
end

if __FILE__ == $0
  API_VERSION = :v201708

  # The criterion type to be used for DSA page feeds. DSA page feeds use
  # criterionType field instead of the placeholderType field unlike most other
  # feed types.
  DSA_PAGE_FEED_CRITERION_TYPE = 61

  # ID that corresponds to the page URLs.
  DSA_PAGE_URLS_FIELD_ID = 1

  # ID that corresponds to the labels.
  DSA_LABEL_FIELD_ID = 2

  begin
    campaign_id = 'INSERT_CAMPAIGN_ID_HERE'.to_i
    ad_group_id = 'INSERT_AD_GROUP_ID_HERE'.to_i

    add_dynamic_page_feed(campaign_id, 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 a DSA campaign

#!/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 code example adds a Dynamic Search Ads campaign. To get campaigns, run
# get_campaigns.rb.

require 'date'

require 'adwords_api'

def add_dynamic_search_ads_campaign()
  # 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')

  budget = create_budget(adwords)

  campaign = create_campaign(adwords, budget)
  ad_group = create_ad_group(adwords, campaign)
  create_expanded_dsa(adwords, ad_group)
  add_web_page_criteria(adwords, ad_group)
end

def create_budget(adwords)
  budget_srv = adwords.service(:BudgetService, API_VERSION)

  shared_budget = {
    :name => "Interplanetary Cruise #%d" % (Time.now.to_f * 1000).to_i,
    :amount => {
      :micro_amount => 50_000_000
    },
    :delivery_method => 'STANDARD'
  }

  budget_operation = {
    :operand => shared_budget,
    :operator => 'ADD'
  }

  budget = budget_srv.mutate([budget_operation])[:value].first

  return budget
end

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

  campaign = {
    :name => "Interplanetary Cruise #%d" % (Time.now.to_f * 1000).to_i,
    :advertising_channel_type => 'SEARCH',
    # 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.
    :status => 'PAUSED',
    :bidding_strategy_configuration => {
      :bidding_strategy_type => 'MANUAL_CPC'
    },
    # Only the budgetId should be sent; all other fields will be ignored by
    # CampaignService.
    :budget => {
      :budget_id => budget[:budget_id]
    },
    :settings => [
      :xsi_type => 'DynamicSearchAdsSetting',
      :domain_name => 'example.com',
      :language_code => 'en'
    ],
    # Optional: Set the start and end dates.
    :start_date => DateTime.parse((Date.today + 1).to_s).strftime('%Y%m%d'),
    :end_date => DateTime.parse(Date.today.next_year.to_s).strftime('%Y%m%d')
  }

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

  new_campaign = campaign_srv.mutate([operation])[:value].first
  puts "Campaign with name '%s' and ID %d was added." %
      [new_campaign[:name], new_campaign[:id]]

  return new_campaign
end

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

  ad_group = {
    # Required: Set the ad group's tpe to Dynamic Search Ads.
    :ad_group_type => 'SEARCH_DYNAMIC_ADS',
    :name => "Earth to Mars Cruises #%d" % (Time.now.to_f * 1000).to_i,
    :campaign_id => campaign[:id],
    :status => 'PAUSED',
    # Recommended: Set a tracking URL template for your ad group if you want to
    # use URL tracking software.
    :tracking_url_template =>
        'http://tracker.example.com/traveltracker/{escapedlpurl}',
    :bidding_strategy_configuration => {
      :bids => [{
        :xsi_type => 'CpcBid',
        :bid => {
          :micro_amount => 3_000_000
        }
      }]
    }
  }

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

  new_ad_group = ad_group_srv.mutate([operation])[:value].first
  puts "Ad group with name '%s' and ID %d was added." %
      [new_ad_group[:name], new_ad_group[:id]]

  return new_ad_group
end

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

  # Create the expanded Dynamic Search Ad. This ad will have its headline and
  # final URL auto-generated at serving time according to domain name specific
  # information provided by DynamicSearchAdsSetting at the campaign level.
  expanded_dsa = {
    :xsi_type => 'ExpandedDynamicSearchAd',
    :description => 'Buy your tickets now!'
  }

  ad_group_ad = {
    :ad_group_id => ad_group[:id],
    :ad => expanded_dsa,
    # Optional: Set the status.
    :status => 'PAUSED'
  }

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

  new_ad_group_ad = ad_group_ad_srv.mutate([operation])[:value].first
  new_expanded_dsa = new_ad_group_ad[:ad]
  puts "Expanded Dynamic Search Ad with ID %d and description '%s' was added." %
      [new_expanded_dsa[:id], new_expanded_dsa[:description]]
end

def add_web_page_criteria(adwords, ad_group)
  ad_group_criterion_srv =
      adwords.service(:AdGroupCriterionService, API_VERSION)

  webpage = {
    :xsi_type => 'Webpage',
    :parameter => {
      :criterion_name => 'Special offers',
      :conditions => [
        {
          :operand => 'URL',
          :argument => '/specialoffers'
        },
        {
          :operand => 'PAGE_TITLE',
          :argument => 'Special Offer'
        }
      ]
    }
  }

  biddable_ad_group_criterion = {
    :xsi_type => 'BiddableAdGroupCriterion',
    :ad_group_id => ad_group[:id],
    :criterion => webpage,
    :user_status => 'PAUSED',
    # Optional: set a custom bid.
    :bidding_strategy_configuration => {
      :bids => [{
        :xsi_type => 'CpcBid',
        :bid => {
          :micro_amount => 10_000_000
        }
      }]
    }
  }

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

  new_ad_group_criterion =
      ad_group_criterion_srv.mutate([operation])[:value].first
  puts "Webpage criterion with ID %d was added to ad group ID %d." % [
    new_ad_group_criterion[:criterion][:id],
    new_ad_group_criterion[:ad_group_id]
  ]
end

if __FILE__ == $0
  API_VERSION = :v201708

  begin
    add_dynamic_search_ads_campaign()

  # 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 an expanded text ad with Upgraded URLs

#!/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 code example adds an expanded text ad that uses advanced features of
# upgraded URLs.

require 'adwords_api'

def add_text_ad_with_upgraded_urls(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_ad_srv = adwords.service(:AdGroupAdService, API_VERSION)

  # Since your tracking URL will have two custom parameters, provide their
  # values too. This can be provided at campaign, ad group, ad, criterion, or
  # feed item levels.
  season_parameter = {
    :key => 'season',
    :value => 'christmas'
  }

  promo_code_parameter = {
    :key => 'promocode',
    :value => 'NYC123'
  }

  tracking_url_parameters = {
    :parameters => [season_parameter, promo_code_parameter]
  }

  expanded_text_ad = {
    :xsi_type => 'ExpandedTextAd',
    :headline_part1 => 'Luxury Cruise to Mars',
    :headline_part2 => 'Visit the Red Planet in style.',
    :description => 'Low-gravity fun for everyone!',
    # Specify a tracking URL for 3rd party tracking provider. You may specify
    # one at customer, campaign, ad group, ad, criterion, or feed item levels.
    :tracking_url_template => 'http://tracker.example.com/' +
        '?season={_season}&promocode={_promocode}&u={lpurl}',
    :url_custom_parameters => tracking_url_parameters,
    # Specify a list of final URLs. This field cannot be set if :url field
    # is set. This may be specified at ad, criterion, and feed item levels.
    :final_urls => [
      'http://www.example.com/cruise/space/',
      'http://www.example.com/locations/mars/'
    ],
    # Specify a list of final mobile URLs. This field cannot be set if :url
    # field is set. This may be specificed at ad, criterion, and
    # feed item levels.
    :final_mobile_urls => [
      'http://mobile.example.com/cruise/space/',
      'http://mobile.example.com/locations/mars/'
    ]
  }

  operation = {
    :operator => 'ADD',
    :operand => {:ad_group_id => ad_group_id, :ad => expanded_text_ad}
  }

  # Add ads.
  response = ad_group_ad_srv.mutate([operation])
  if response and response[:value]
    response[:value].each do |ad|
      text_ad = ad[:ad]
      puts "Ad with ID %d was added." % [text_ad[:id]]
      puts "\tFinal URLs: %s" % [text_ad[:final_urls].join(', ')]
      puts "\tFinal Mobile URLs: %s" % [text_ad[:final_mobile_urls].join(', ')]
      puts "\tTracking URL template: %s" % [text_ad[:tracking_url_template]]
      custom_parameters =
          text_ad[:url_custom_parameters][:parameters].map do |custom_parameter|
            "%s=%s" % [custom_parameter[:key], custom_parameter[:value]]
          end
      puts "\tCustom parameters: %s" % [custom_parameters.join(', ')]
    end
  else
    raise StandardError, 'No ads were added.'
  end
end

if __FILE__ == $0
  API_VERSION = :v201708

  begin
    ad_group_id = 'INSERT_AD_GROUP_ID_HERE'.to_i
    add_text_ad_with_upgraded_urls(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 an HTML 5 ad to an ad group

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright:: Copyright 2015, 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 code example adds an HTML5 ad to a given ad group.
# To get ad groups, run basic_operations/get_ad_groups.rb.

require 'adwords_api'
require 'base64'

def add_html5_ad(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_ad_srv = adwords.service(:AdGroupAdService, API_VERSION)

  # The HTML5 zip file contains all the HTML, CSS, and images needed for the
  # HTML5 ad. For help on creating an HTML5 zip file, check out Google Web
  # Designer (https://www.google.com/webdesigner).
  html5_url = 'http://goo.gl/9Y7qI2'
  html5_data = AdsCommon::Http.get(html5_url, adwords.config)
  html5_data_base64 = Base64.encode64(html5_data)

  # Create a media bundle containing the zip file with all the HTML5
  # components.
  media_bundle = {
    :xsi_type => 'MediaBundle',
    :data => html5_data_base64,
    :entry_point => 'carousel/index.html',
    :type => 'MEDIA_BUNDLE'
  }

  # Create the template elements for the ad. You can refer to
  # https://developers.google.com/adwords/api/docs/appendix/templateads
  # for the list of available template fields.
  ad_data = {
    :unique_name => 'adData',
    :fields => [
      {
        :name => 'Custom_layout',
        :field_media => media_bundle,
        :type => 'MEDIA_BUNDLE'
      },
      {
        :name => 'layout',
        :field_text => 'Custom',
        :type => 'ENUM'
      }
    ]
  }

  html5_ad = {
    :xsi_type => 'TemplateAd',
    :name => 'Ad for HTML5',
    :template_id => 419,
    :final_urls => ['http://example.com/html5'],
    :display_url => 'www.example.com/html5',
    :dimensions => {
      :width => 300,
      :height => 250
    },
    :template_elements => [ad_data]
  }

  ad_group_ad = {
    :ad_group_id => ad_group_id,
    :ad => html5_ad,
    :status => 'PAUSED'
  }

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

  response = ad_group_ad_srv.mutate([operation])
  if response and !response.empty?
    response[:value].each do |ad_group_ad|
      puts "New HTML5 ad with ID %d and display url '%s' was added." %
          [ad_group_ad[:ad][:id], ad_group_ad[:ad][:display_url]]
    end
  else
    puts "No HTML5 ads were added."
  end
end

if __FILE__ == $0
  API_VERSION = :v201708

  begin
    # Ad group ID to add text ads to.
    ad_group_id = 'INSERT_AD_GROUP_ID_HERE'.to_i
    add_html5_ad(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 responsive ads

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright:: Copyright 2016, 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 code example adds an image representing the ad using the MediaService and
# then adds a responsive display ad to a given ad group.
# To get ad groups, run basic_operations/get_ad_groups.rb.

require 'adwords_api'
require 'base64'

def add_responsive_display_ad(adwords, ad_group_id)
  ad_group_ad_srv = adwords.service(:AdGroupAdService, API_VERSION)

  # This ad format does not allow the creation of an image using the Image.data
  # field. An image must first be created using the MediaService, and
  # Image.mediaId must be populated when creating the ad.
  ad_image = upload_image(adwords, 'https://goo.gl/3b9Wfh')

  # Create the responsive display ad.
  responsive_display_ad = {
    :xsi_type => 'ResponsiveDisplayAd',
    # This ad format does not allow the creation of an image using the
    # Image.data field. An image must first be created using the MediaService,
    # and Image.mediaId must be populated when creating the ad.
    :marketing_image => {:media_id => ad_image[:media_id]},
    :short_headline => 'Travel',
    :long_headline => 'Traver the World',
    :description => 'Take to the air!',
    :business_name => 'Interplanetary Cruises',
    :final_urls => ['http://www.example.com/'],

    # Optional: Set call to action text.
    :call_to_action_text => 'Shop Now'

    # Whitelisted accounts only: Set color settings using hexadecimal values.
    # Set 'allow_flexible_color' to false if you want your ads to render by
    # always using your colors strictly.
    #:main_color => '#0000ff',
    #:accent_color => '#ffff00',
    #:allow_flexible_color => false,

    # Whitelisted accounts only: Set the format setting that the ad will be
    # served in.
    #:format_setting => 'NON_NATIVE'
  }

  # Optional: Create a square marketing image using MediaService, and set it to
  # the ad.
  square_image = upload_image(adwords, 'https://goo.gl/mtt54n')
  responsive_display_ad[:square_marketing_image] = {
    :media_id => square_image[:media_id]
  }

  # Optional: Set dynamic display ad settings, composed of landscape logo image,
  # promotion text, and price prefix.
  logo_image = upload_image(adwords, 'https://goo.gl/dEvQeF')
  responsive_display_ad[:dynamic_display_ad_settings] = {
    :landscape_logo_image => {:media_id => logo_image[:media_id]},
    :price_prefix => 'as low as',
    :promo_text => 'Free shipping!'
  }

  # Create an ad group ad for the responsive display ad.
  responsive_display_ad_group_ad = {
    :ad_group_id => ad_group_id,
    :ad => responsive_display_ad,
    # Additional propertires (non-required).
    :status => 'PAUSED'
  }

  # Create operation.
  responsive_display_ad_group_ad_operations = {
    :operator => 'ADD',
    :operand => responsive_display_ad_group_ad
  }

  # Add the responsive display ad.
  result = ad_group_ad_srv.mutate([responsive_display_ad_group_ad_operations])

  # Display results.
  if result && result[:value]
    result[:value].each do |ad_group_ad|
      puts ("New responsive display ad with id %d and short headline '%s' was" +
          "added.") % [ad_group_ad[:ad][:id], ad_group_ad[:ad][:short_headline]]
    end
  else
    puts 'No responsive display ads were added.'
  end
end

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

  # Create an image.
  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])
  if response and !response.empty?
    return response.first
  else
    raise StandardError, 'Could not upload image, aborting.'
  end
end

if __FILE__ == $0
  API_VERSION = :v201708

  begin
    # 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 ID to add text ads to.
    ad_group_id = 'INSERT_AD_GROUP_ID_HERE'.to_i
    add_responsive_display_ad(adwords, 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 a universal app campaign

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright:: Copyright 2016, 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 universal app campaign.
#
# To get campaigns, run get_campaigns.rb. To upload image assets for this
# campaign, run upload_image.rb.

require 'adwords_api'
require 'date'

def add_universal_app_campaigns()
  # 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)
  budget_srv = adwords.service(:BudgetService, API_VERSION)

  # Create a budget.
  budget = {
    :name => 'Interplanetary budget #%d' % (Time.new.to_f * 1000).to_i,
    :amount => {:micro_amount => 50000000},
    :delivery_method => 'STANDARD',
    # Universal app campaigns don't support shared budgets.
    :is_explicitly_shared => false
  }
  budget_operation = {:operator => 'ADD', :operand => budget}

  # Add the budget.
  return_budget = budget_srv.mutate([budget_operation])
  budget_id = return_budget[:value].first[:budget_id]

  # Create campaigns.
  universal_app_campaign = {
    :name => "Interplanetary Cruise #%d" % (Time.new.to_f * 1000).to_i,
    # Recommendation: Set the campaign to PAUSED when creating it to stop the
    # ads from immediately serving. Set it to ENABLED once you've added
    # targeting and the ads are ready to serve.
    :status => 'PAUSED',
    # Set the advertising channel and subchannel types for universal app
    # campaigns.
    :advertising_channel_type => 'MULTI_CHANNEL',
    :advertising_channel_sub_type => 'UNIVERSAL_APP_CAMPAIGN',
    # Set the campaign's bidding strategy. Universal app campaigns only support
    # the TARGET_CPA bidding strategy.
    :bidding_strategy_configuration => {
      :bidding_scheme => {
        :xsi_type => 'TargetCpaBiddingScheme',
        :target_cpa => {
          :micro_amount => 1000000
        }
      }
    },
    # Budget (required) - note only the budget ID is required.
    :budget => {:budget_id => budget_id},
    # Optional fields:
    :start_date =>
        DateTime.parse((Date.today + 1).to_s).strftime('%Y%m%d'),
    :end_date =>
        DateTime.parse((Date.today + 365).to_s).strftime('%Y%m%d')
  }

  universal_app_setting = {
    # Set the campaign's assets and ad text ideas. These values will be used to
    # generate ads.
    :xsi_type => 'UniversalAppCampaignSetting',
    :app_id => 'com.labpixies.colordrips',
    :description1 => 'A cool puzzle game',
    :description2 => 'Remove connected blocks',
    :description3 => '3 difficulty levels',
    :description4 => '4 colorful fun skins'
    # Optional: You can set up to 10 image assets for your campaign. See
    # upload_image.rb for an example on how to upload images.
    #
    # :image_media_ids => [INSERT_IMGAGE_MEDIA_ID(s)_HERE]
  }

  # Optimize this campaign for getting new users for your app.
  universal_app_setting[:universal_app_bidding_strategy_goal_type] =
      'OPTIMIZE_FOR_INSTALL_CONVERSION_VOLUME'

  # Optional: If you select OPTIMIZE_FOR_IN_APP_CONVERSION_VOLUME goal type,
  # then also specify your in-app conversion types so AdWords can focus your
  # campaign on people who are most likely to complete the corresponding in-app
  # actions.
  # Conversion type IDs can be retrieved using ConversionTrackerService.get.
  #
  # universal_app_campaign[:selective_optimization] = {
  #   :conversion_type_ids => [INSERT_CONVERSION_TYPE_ID(s)_HERE]
  # }

  # Optional: Set the campaign settings for Advanced location options.
  geo_setting = {
    :xsi_type => 'GeoTargetTypeSetting',
    :positive_geo_target_type => 'DONT_CARE',
    :negative_geo_target_type => 'DONT_CARE'
  }

  universal_app_campaign[:settings] = [
    universal_app_setting,
    geo_setting
  ]

  # Construct the operation and add the campaign.
  operations = [{
    :operator => 'ADD',
    :operand => universal_app_campaign
  }]

  campaigns = campaign_srv.mutate(operations)[:value]

  if campaigns
    campaigns.each do |campaign|
      puts "Universal app campaign with name '%s' and ID %d was added." %
          [campaign[:name], campaign[:id]]
      set_campaign_targeting_criteria(adwords, campaign)
    end
  else
    raise new StandardError, 'No universal app campaigns were added.'
  end
end

def set_campaign_targeting_criteria(adwords, campaign)
  campaign_criterion_service =
      adwords.service(:CampaignCriterionService, API_VERSION)

  criteria = [
    {
      :xsi_type => 'Location',
      :id => 21137 # California
    },
    {
      :xsi_type => 'Location',
      :id => 2484 # Mexico
    },
    {
      :xsi_type => 'Language',
      :id => 1000 # English
    },
    {
      :xsi_type => 'Language',
      :id => 1003 # Spanish
    }
  ]

  operations = criteria.map do |criterion|
    {
      :operator => 'ADD',
      :operand => {
        :campaign_id => campaign[:id],
        :criterion => criterion
      }
    }
  end

  response = campaign_criterion_service.mutate(operations)

  if response and response[:value]
    # Display the added campaign targets.
    response[:value].each do |criterion|
      puts 'Campaign criteria of type "%s" and id %d was added.' % [
          criterion[:criterion][:criterion_type],
          criterion[:criterion][:id]
      ]
    end
  end
end

if __FILE__ == $0
  API_VERSION = :v201708

  begin
    add_universal_app_campaigns()

  # 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

Create and attach a shared keyword set

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright:: Copyright 2015, 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 shared list of negative broad match keywords, it then
# attaches them to a campaign.

require 'adwords_api'
require 'date'

def create_and_attach_shared_keyword_set(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')

  shared_set_srv = adwords.service(:SharedSetService, API_VERSION)
  shared_criterion_srv = adwords.service(:SharedCriterionService, API_VERSION)
  campaign_shared_set_srv =
      adwords.service(:CampaignSharedSetService, API_VERSION)

  # Keywords to create a shared set of.
  keywords = ['mars cruise', 'mars hotels']

  # Create shared negative keyword set.
  shared_set = {
    :name => 'API Negative keyword list - %d' % (Time.new.to_f * 1000).to_i,
    :type => 'NEGATIVE_KEYWORDS'
  }

  # Add shared set.
  response = shared_set_srv.mutate([
      {:operator => 'ADD', :operand => shared_set}
  ])

  if response and response[:value]
    shared_set = response[:value].first
    shared_set_id = shared_set[:shared_set_id]
    puts "Shared set with ID %d and name '%s' was successfully added." %
        [shared_set_id, shared_set[:name]]
  else
    raise StandardError, 'No shared set was added'
  end

  # Add negative keywords to shared set.
  shared_criteria = keywords.map do |keyword|
    {
      :criterion => {
        :xsi_type => 'Keyword',
        :text => keyword,
        :match_type => 'BROAD'
      },
      :negative => true,
      :shared_set_id => shared_set_id
    }
  end

  operations = shared_criteria.map do |criterion|
    {:operator => 'ADD', :operand => criterion}
  end

  response = shared_criterion_srv.mutate(operations)
  if response and response[:value]
    response[:value].each do |shared_criterion|
      puts "Added shared criterion ID %d '%s' to shared set with ID %d." %
          [shared_criterion[:criterion][:id],
           shared_criterion[:criterion][:text],
           shared_criterion[:shared_set_id]]
    end
  else
    raise StandardError, 'No shared keyword was added'
  end

  # Attach the articles to the campaign.
  campaign_set = {
    :campaign_id => campaign_id,
    :shared_set_id => shared_set_id
  }

  response = campaign_shared_set_srv.mutate([
    {:operator => 'ADD', :operand => campaign_set}
  ])
  if response and response[:value]
    campaign_shared_set = response[:value].first
    puts 'Shared set ID %d was attached to campaign ID %d' %
        [campaign_shared_set[:shared_set_id], campaign_shared_set[:campaign_id]]
  else
    raise StandardError, 'No campaign shared set was added'
  end
end

if __FILE__ == $0
  API_VERSION = :v201708

  begin
    # Campaign ID to attach shared keyword set to.
    campaign_id = 'INSERT_CAMPAIGN_ID_HERE'.to_i

    create_and_attach_shared_keyword_set(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

Find and remove criteria from a shared set

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright:: Copyright 2015, 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 demonstrates how to find shared sets, shared set criterions and
# how to remove them.

require 'adwords_api'

def find_and_remove_criteria_from_shared_set(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_shared_set_srv =
      adwords.service(:CampaignSharedSetService, API_VERSION)
  shared_criterion_srv = adwords.service(:SharedCriterionService, API_VERSION)

  shared_set_ids = []
  criterion_ids = []

  # First, retrieve all shared sets associated with the campaign.

  # Create selector for shared sets to:
  # - filter by campaign ID,
  # - filter by shared set type.
  selector = {
    :fields => ['SharedSetId', 'CampaignId', 'SharedSetName', 'SharedSetType',
        'Status'],
    :predicates => [
      {:field => 'CampaignId', :operator => 'EQUALS', :values => [campaign_id]},
      {:field => 'SharedSetType', :operator => 'IN', :values =>
          ['NEGATIVE_KEYWORDS', 'NEGATIVE_PLACEMENTS']}
    ],
    :paging => {
      :start_index => 0,
      :number_results => PAGE_SIZE
    }
  }

  # Set initial values.
  offset, page = 0, {}

  begin
    page = campaign_shared_set_srv.get(selector)
    if page[:entries]
      page[:entries].each do |shared_set|
        puts "Campaign shared set ID %d and name '%s'" %
            [shared_set[:shared_set_id], shared_set[:shared_set_name]]
        shared_set_ids << shared_set[:shared_set_id]
      end
      # Increment values to request the next page.
      offset += PAGE_SIZE
      selector[:paging][:start_index] = offset
    end
  end while page[:total_num_entries] > offset

  # Next, Retrieve criterion IDs for all found shared sets.
  selector = {
    :fields => ['SharedSetId', 'Id', 'KeywordText', 'KeywordMatchType',
        'PlacementUrl'],
    :predicates => [
      {:field => 'SharedSetId', :operator => 'IN', :values => shared_set_ids}
    ],
    :paging => {
      :start_index => 0,
      :number_results => PAGE_SIZE
    }
  }

  # Set initial values.
  offset, page = 0, {}

  begin
    page = shared_criterion_srv.get(selector)
    if page[:entries]
      page[:entries].each do |shared_criterion|
        if shared_criterion[:criterion][:type] == 'KEYWORD'
          puts "Shared negative keyword with ID %d and text '%s' was found." %
            [shared_criterion[:criterion][:id],
             shared_criterion[:criterion][:text]]
        elsif shared_criterion[:criterion][:type] == 'PLACEMENT'
          puts "Shared negative placement with ID %d and url '%s' was found." %
            [shared_criterion[:criterion][:id],
             shared_criterion[:criterion][:url]]
        else
          puts 'Shared criterion with ID %d was found.' %
            [shared_criterion[:criterion][:id]]
        end
        criterion_ids << {
           :shared_set_id => shared_criterion[:shared_set_id],
           :criterion_id => shared_criterion[:criterion][:id]
        }
      end
      # Increment values to request the next page.
      offset += PAGE_SIZE
      selector[:paging][:start_index] = offset
    end
  end while page[:total_num_entries] > offset

  # Finally, remove the criteria.
  operations = criterion_ids.map do |criterion|
    {
      :operator => 'REMOVE',
      :operand => {
        :criterion => {:id => criterion[:criterion_id]},
        :shared_set_id => criterion[:shared_set_id]
      }
    }
  end

  response = shared_criterion_srv.mutate(operations)
  if response and response[:value]
    response[:value].each do |criterion|
      puts "Criterion ID %d was successfully removed from shared set ID %d." %
          [criterion[:criterion][:id], criterion[:shared_set_id]]
    end
  else
    puts 'No shared criteria were removed.'
  end
end

if __FILE__ == $0
  API_VERSION = :v201708
  PAGE_SIZE = 500

  begin
    # ID of a campaign to remove shared criteria from.
    campaign_id = 'INSERT_CAMPAIGN_ID_HERE'.to_i

    find_and_remove_criteria_from_shared_set(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

Get ad group bid modifiers

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright:: Copyright 2013, 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 illustrates how to retrieve ad group level bid modifiers for a
# campaign.

require 'adwords_api'

def get_ad_group_bid_modifiers(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')

  bid_modifier_srv = adwords.service(:AdGroupBidModifierService, API_VERSION)

  # Get all ad group bid modifiers for the campaign.
  selector = {
    :fields => ['CampaignId', 'AdGroupId', 'Id', 'BidModifier'],
    :predicates => [
      {:field => 'CampaignId', :operator => 'EQUALS', :values => [campaign_id]}
    ],
    :paging => {
      :start_index => 0,
      :number_results => PAGE_SIZE
    }
  }

  # Set initial values.
  offset, page = 0, {}

  begin
    page = bid_modifier_srv.get(selector)
    if page[:entries]
      page[:entries].each do |modifier|
        value = modifier[:bid_modifier] || 'unset'
        puts ('Campaign ID %d, AdGroup ID %d, Criterion ID %d has ad group ' +
           'level modifier: %s') %
           [modifier[:campaign_id], modifier[:ad_group_id],
            modifier[:criterion][:id], value]
      end
      # Increment values to request the next page.
      offset += PAGE_SIZE
      selector[:paging][:start_index] = offset
    else
      puts 'No ad group level bid overrides returned.'
    end
  end while page[:total_num_entries] > offset
end

if __FILE__ == $0
  API_VERSION = :v201708
  PAGE_SIZE = 500

  begin
    # ID of a campaign to pull overrides for.
    campaign_id = 'INSERT_CAMPAIGN_ID_HERE'.to_i

    get_ad_group_bid_modifiers(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

Use a portfolio bidding strategy

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright:: Copyright 2013, 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 Portfolio Bidding Strategy and uses it to construct a
# campaign.

require 'adwords_api'
require 'date'

def use_portfolio_bidding_strategy()
  # 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')

  budget_srv = adwords.service(:BudgetService, API_VERSION)
  bidding_srv = adwords.service(:BiddingStrategyService, API_VERSION)
  campaign_srv = adwords.service(:CampaignService, API_VERSION)

  # Create a budget, which can be shared by multiple campaigns.
  budget = {
    :name => 'Interplanetary budget #%d' % (Time.new.to_f * 1000).to_i,
    :amount => {:micro_amount => 50000000},
    :delivery_method => 'STANDARD',
    :is_explicitly_shared => true
  }
  return_budget = budget_srv.mutate([
    {:operator => 'ADD', :operand => budget}])
  budget_id = return_budget[:value].first[:budget_id]

  # Create a portfolio bidding strategy.
  portfolio_bidding_strategy = {
    :name => 'Maximize Clicks #%d' % (Time.new.to_f * 1000).to_i,
    :bidding_scheme => {
      :xsi_type => 'TargetSpendBiddingScheme',
      # Optionally set additional bidding scheme parameters.
      :bid_ceiling => {:micro_amount => 20000000},
      :spend_target => {:micro_amount => 40000000}
    }
  }
  return_strategy = bidding_srv.mutate([
    {:operator => 'ADD', :operand => portfolio_bidding_strategy}])

  bidding_strategy = return_strategy[:value].first
  puts ("Portfolio bidding strategy with name '%s' and ID %d of type '%s'" +
      ' was created') %
      [bidding_strategy[:name], bidding_strategy[:id], bidding_strategy[:type]]

  # Create campaigns.
  campaigns = [
    {
      :name => "Interplanetary Cruise #%d" % (Time.new.to_f * 1000).to_i,
      # 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',
      :bidding_strategy_configuration => {
        :bidding_strategy_id => bidding_strategy[:id]
      },
      # Budget (required) - note only the budget ID is required.
      :budget => {:budget_id => budget_id},
      :advertising_channel_type => 'SEARCH',
      :network_setting => {
        :target_google_search => true,
        :target_search_network => true,
        :target_content_network => true
      }
    },
    {
      :name => "Interplanetary Cruise banner #%d" % (Time.new.to_f * 1000).to_i,
      :status => 'PAUSED',
      :bidding_strategy_configuration => {
        :bidding_strategy_id => bidding_strategy[:id]
      },
      :budget => {:budget_id => budget_id},
      :advertising_channel_type => 'DISPLAY',
      :network_setting => {
        :target_google_search => false,
        :target_search_network => false,
        :target_content_network => true
      }
    }
  ]

  # Prepare for adding campaign.
  operations = campaigns.map do |campaign|
    {:operator => 'ADD', :operand => campaign}
  end

  # Add campaign.
  response = campaign_srv.mutate(operations)
  if response and response[:value]
    response[:value].each do |campaign|
      puts "Campaign with name '%s' and ID %d was added." %
          [campaign[:name], campaign[:id]]
    end
  else
    raise new StandardError, 'No campaigns were added.'
  end
end

if __FILE__ == $0
  API_VERSION = :v201708

  begin
    use_portfolio_bidding_strategy()

  # 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

Setup OAuth2

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright:: Copyright 2013, 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 illustrates how to set up OAuth2 authentication credentials.

require 'adwords_api'

def setup_oauth2()
  # 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')

  # You can call authorize explicitly to obtain the access token. Otherwise, it
  # will be invoked automatically on the first API call.
  # There are two ways to provide verification code, first one is via the block:
  token = adwords.authorize() do |auth_url|
    puts "Hit Auth error, please navigate to URL:\n\t%s" % auth_url
    print 'log in and type the verification code: '
    verification_code = gets.chomp
    verification_code
  end
  if token
    print "\nWould you like to update your adwords_api.yml to save " +
        "OAuth2 credentials? (y/N): "
    response = gets.chomp
    if ('y'.casecmp(response) == 0) or ('yes'.casecmp(response) == 0)
      adwords.save_oauth2_token(token)
      puts 'OAuth2 token is now saved to ~/adwords_api.yml and will be ' +
          'automatically used by the library.'
    end
  end

  # Alternatively, you can provide one within the parameters:
  # token = adwords.authorize({:oauth2_verification_code => verification_code})

  # Note, 'token' is a Hash. Its value is not used in this example. If you need
  # to be able to access the API in offline mode, with no user present, you
  # should persist it to be used in subsequent invocations like this:
  # adwords.authorize({:oauth2_token => token})

  # No exception thrown - we are good to make a request.
end

if __FILE__ == $0
  API_VERSION = :v201708

  begin
    setup_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

Use OAuth2 with a Service Account

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright:: Copyright 2012, 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 illustrates how to use OAuth2 authentication method with
# Service Account (JWT). For this example to work, your Service Account must be
# a G Suite account.
#
# See https://developers.google.com/adwords/api/docs/guides/service-accounts
# for more information.

require 'adwords_api'

def use_oauth2_jwt()
  # 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')

  # Option 1: provide key filename as authentication -> oauth2_keyfile in the
  #           configuration file. No additional code is necessary.
  # To provide a file name at runtime, use authorize:
  # adwords.authorize({:oauth2_keyfile => key_filename})

  # Option 2: retrieve key manually and create OpenSSL::PKCS12 object.
  # key_filename = 'INSERT_FILENAME_HERE'
  # key_secret = 'INSERT_SECRET_HERE'
  # key_file_data = File.read(key_filename)
  # key = OpenSSL::PKCS12.new(key_file_data, key_secret).key
  # adwords.authorize({:oauth2_key => key})

  # Now you can make API calls.
  campaign_srv = adwords.service(:CampaignService, API_VERSION)

  # Get all the campaigns for this account; empty selector.
  selector = {
    :fields => ['Id', 'Name', 'Status'],
    :ordering => [
      {:field => 'Name', :sort_order => 'ASCENDING'}
    ]
  }

  response = campaign_srv.get(selector)
  if response and response[:entries]
    campaigns = response[:entries]
    campaigns.each do |campaign|
      puts "Campaign ID %d, name '%s' and status '%s'" %
          [campaign[:id], campaign[:name], campaign[:status]]
    end
  else
    puts 'No campaigns were found.'
  end
end

if __FILE__ == $0
  API_VERSION = :v201708

  begin
    use_oauth2_jwt()

  # 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

Use runtime configuration

#!/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 demonstrates how to make AdWords queries without using the
# adwords_api.yml file.

require 'adwords_api'
require 'date'

def use_runtime_config(client_id, client_secret, refresh_token,
        developer_token, client_customer_id, user_agent)
  # AdwordsApi::Api will read a config file from ENV['HOME']/adwords_api.yml
  # when called without parameters.
  adwords = AdwordsApi::Api.new({
    :authentication => {
      :method => 'OAuth2',
      :oauth2_client_id => client_id,
      :oauth2_client_secret => client_secret,
      :developer_token => developer_token,
      :client_customer_id => client_customer_id,
      :user_agent => user_agent,
      :oauth2_token => {
        :refresh_token => refresh_token
      }
    },
    :service => {
      :environment => 'PRODUCTION'
    }
  })

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

  customer_srv = adwords.service(:CustomerService, API_VERSION)
  customer = customer_srv.get()
  puts "You are logged in as customer: %d" % customer[:id]
end

if __FILE__ == $0
  API_VERSION = :v201708

  begin
    client_id = 'INSERT_CLIENT_ID_HERE'
    client_secret = 'INSERT_CLIENT_SECRET_HERE'
    refresh_token = 'INSERT_REFRESH_TOKEN_HERE'
    developer_token = 'INSERT_DEVELOPER_TOKEN_HERE'
    client_customer_id = 'INSERT_CLIENT_CUSTOMER_ID_HERE'
    user_agent = 'INSERT_USER_AGENT_HERE'

    use_runtime_config(client_id, client_secret, refresh_token,
        developer_token, client_customer_id, user_agent)

  # 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.