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

Reporting Samples

The code samples below provide examples of common reporting functions using the AdWords API. Client Library.

Download a criteria performance report with AWQL

#!/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 gets an Ad Hoc report using AdWords Query Language.
# See AWQL guide for more details:
#   https://developers.google.com/adwords/api/docs/guides/awql

require 'date'

require 'adwords_api'

def download_criteria_report_with_awql(file_name, report_format)
  # 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')

  # Get report utilities for the version.
  report_utils = adwords.report_utils(API_VERSION)

  # Prepare a date range for the last week.
  start_date =  DateTime.parse((Date.today - 7).to_s).strftime('%Y%m%d')
  end_date = DateTime.parse((Date.today - 1).to_s).strftime('%Y%m%d')

  # Define report definition. You can also pass your own XML text as a string.
  report_query_builder = adwords.report_query_builder do |b|
    b.select(*%w[CampaignId AdGroupId Id Criteria CriteriaType Impressions
        Clicks Cost])
    b.from('CRITERIA_PERFORMANCE_REPORT')
    b.where('Status').in('ENABLED', 'PAUSED')
    # You could use the during_date_range method to specify ranges such as
    # 'LAST_7_DAYS', but you can't specify both.
    b.during(start_date, end_date)
  end
  report_query = report_query_builder.build.to_s

  # Optional: Set the configuration of the API instance to suppress header,
  # column name, or summary rows in the report output. You can also configure
  # this in your adwords_api.yml configuration file.
  adwords.skip_report_header = false
  adwords.skip_column_header = false
  adwords.skip_report_summary = false
  # Enable to allow rows with zero impressions to show.
  adwords.include_zero_impressions = true

  # Download report, using "download_report_as_file_with_awql" utility method.
  # To retrieve the report as return value, use "download_report_with_awql"
  # method.
  report_utils.download_report_as_file_with_awql(report_query, report_format,
                                                 file_name)
  puts "Report was downloaded to '%s'." % file_name
end

if __FILE__ == $0
  API_VERSION = :v201809

  begin
    # File name to write report to.
    file_name = 'INSERT_OUTPUT_FILE_NAME_HERE'
    report_format = 'CSV'
    download_criteria_report_with_awql(file_name, report_format)

  # 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::ReportError => e
    puts 'Reporting Error: %s' % e.message
  end
end

Download a criteria performance report using selectors

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright:: Copyright 2011, 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 gets and downloads an Ad Hoc report from a XML report definition.

require 'adwords_api'

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

  # Get report utilities for the version.
  report_utils = adwords.report_utils(API_VERSION)

  # Define report definition. You can also pass your own XML text as a string.
  report_definition = {
    :selector => {
      :fields => ['CampaignId', 'AdGroupId', 'Id', 'Criteria', 'CriteriaType',
          'FinalUrls', 'Impressions', 'Clicks', 'Cost'],
      # Predicates are optional.
      :predicates => {
        :field => 'Status',
        :operator => 'IN',
        :values => ['ENABLED', 'PAUSED']
      }
    },
    :report_name => 'Last 7 days CRITERIA_PERFORMANCE_REPORT',
    :report_type => 'CRITERIA_PERFORMANCE_REPORT',
    :download_format => 'CSV',
    :date_range_type => 'LAST_7_DAYS',
  }

  # Optional: Set the configuration of the API instance to suppress header,
  # column name, or summary rows in the report output. You can also configure
  # this in your adwords_api.yml configuration file.
  adwords.skip_report_header = false
  adwords.skip_column_header = false
  adwords.skip_report_summary = false
  # Enable to allow rows with zero impressions to show.
  adwords.include_zero_impressions = false

  # Download report, using "download_report_as_file" utility method.
  # To retrieve the report as return value, use "download_report" method.
  report_utils.download_report_as_file(report_definition, file_name)
  puts "Report was downloaded to '%s'." % file_name
end

if __FILE__ == $0
  API_VERSION = :v201809

  begin
    # File name to write report to.
    file_name = 'INSERT_OUTPUT_FILE_NAME_HERE'
    download_criteria_report_with_selector(file_name)

  # 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::ReportError => e
    puts "Reporting Error: %s" % e.message
  end
end

Get the report fields from a report

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright:: Copyright 2011, 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 gets the list of possible report fields for a report type.

require 'adwords_api'

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

  report_def_srv = adwords.service(:ReportDefinitionService, API_VERSION)

  # Get report fields.
  fields = report_def_srv.get_report_fields(report_type)
  if fields
    puts "Report type '%s' contains the following fields:" % report_type
    fields.each do |field|
      puts ' - %s (%s)' % [field[:field_name], field[:field_type]]
      puts '  := [%s]' % field[:enum_values].join(', ') if field[:enum_values]
    end
  end
end

if __FILE__ == $0
  API_VERSION = :v201809

  begin
    report_type = 'INSERT_REPORT_TYPE_HERE'
    get_report_fields(report_type)

  # 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

Download a report for multiple accounts

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright:: Copyright 2011, 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 gets and downloads an Ad Hoc report from a XML report definition
# for all accounts in hierarchy in multiple parallel threads. This example
# needs to be run against an AdWords manager account.

require 'thread'

require 'adwords_api'
require 'adwords_api/utils'

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

  # Determine list of customer IDs to retrieve report for. For this example we
  # will use ManagedCustomerService to get all IDs in hierarchy.

  managed_customer_srv = adwords.service(:ManagedCustomerService, API_VERSION)

  # Get the account hierarchy for this account.
  selector = {:fields => ['CustomerId']}

  graph = managed_customer_srv.get(selector)

  # Using queue to balance load between threads.
  queue = Queue.new()

  if graph and graph[:entries] and !graph[:entries].empty?
    graph[:entries].each {|account| queue << account[:customer_id]}
  else
    raise StandardError, 'Can not retrieve any customer ID'
  end

  # Get report utilities for the version.
  report_utils = adwords.report_utils(API_VERSION)

  # Define report definition. You can also pass your own XML text as a string.
  report_definition = {
    :selector => {
        :fields => ['CampaignId', 'AdGroupId', 'Impressions', 'Clicks', 'Cost'],
        # Predicates are optional.
        :predicates => {
          :field => 'AdGroupStatus',
          :operator => 'IN',
          :values => ['ENABLED', 'PAUSED']
        }
      },
      :report_name => 'Custom ADGROUP_PERFORMANCE_REPORT',
      :report_type => 'ADGROUP_PERFORMANCE_REPORT',
      :download_format => 'CSV',
      :date_range_type => 'LAST_7_DAYS'
  }

  puts 'Retrieving %d reports with %d threads:' % [queue.size, THREADS]

  reports_succeeded = Queue.new()
  reports_failed = Queue.new()

  # Creating a mutex to control access to the queue.
  queue_mutex = Mutex.new

  # Start all the threads.
  threads = (1..THREADS).map do |thread_id|
    Thread.new(report_definition) do |local_def|
      cid = nil
      begin
        cid = queue_mutex.synchronize {(queue.empty?) ? nil : queue.pop(true)}
        if cid
          retry_count = 0
          file_name = 'adgroup_%010d.csv' % cid
          puts "[%2d/%d] Loading report for customer ID %s into '%s'..." %
              [thread_id, retry_count,
               AdwordsApi::Utils.format_id(cid), file_name]
          begin
            report_utils.download_report_as_file(local_def, file_name, cid)
            reports_succeeded << {:cid => cid, :file_name => file_name}
          rescue AdwordsApi::Errors::ReportError => e
            if e.http_code == 500 && retry_count < MAX_RETRIES
              retry_count += 1
              sleep(retry_count * BACKOFF_FACTOR)
              retry
            else
              puts(('Report failed for customer ID %s with code %d after %d ' +
                  'retries.') % [cid, e.http_code, retry_count + 1])
              reports_failed <<
                {:cid => cid, :http_code => e.http_code, :message => e.message}
            end
          end
        end
      end while (cid != nil)
    end
  end

  # Wait for all threads to finish.
  threads.each { |aThread|  aThread.join }

  puts 'Download completed, results:'
  puts 'Successful reports:'
  while !reports_succeeded.empty? do
    result = reports_succeeded.pop()
    puts "\tClient ID %s => '%s'" %
        [AdwordsApi::Utils.format_id(result[:cid]), result[:file_name]]
  end
  puts 'Failed reports:'
  while !reports_failed.empty? do
    result = reports_failed.pop()
    puts "\tClient ID %s => Code: %d, Message: '%s'" %
        [AdwordsApi::Utils.format_id(result[:cid]),
         result[:http_code], result[:message]]
  end
  puts 'End of results.'
end

if __FILE__ == $0
  API_VERSION = :v201809
  # Number of parallel threads to spawn.
  THREADS = 10
  # Maximum number of retries for 500 errors.
  MAX_RETRIES = 5
  # Timeout between retries in seconds.
  BACKOFF_FACTOR = 5

  begin
    parallel_report_download()

  # 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::ReportError => e
    puts 'Reporting Error: %s' % e.message
  end
end

Stream results from a report

#!/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 streams the results of an Ad Hoc report to a file.

require 'adwords_api'

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

  # Get report utilities for the version.
  report_utils = adwords.report_utils(API_VERSION)

  # Define report query.
  report_query_builder = adwords.report_query_builder do |b|
    b.select('Id', 'AdNetworkType1', 'Impressions')
    b.from('CRITERIA_PERFORMANCE_REPORT')
    b.where('Status').in('ENABLED', 'PAUSED')
    b.during_date_range('LAST_7_DAYS')
  end
  report_query = report_query_builder.build.to_s

  # Optional: Set the configuration of the API instance to suppress header,
  # column name, or summary rows in the report output. You can also configure
  # this in your adwords_api.yml configuration file.
  adwords.skip_report_header = true
  adwords.skip_column_header = true
  adwords.skip_report_summary = true
  # Enable to allow rows with zero impressions to show.
  adwords.include_zero_impressions = false

  # Set the default value of the hash to 0 to allow easy totaling.
  ad_network_map = Hash.new(0)

  # We use get_stream_helper_with_awql.each_line here, which uses the
  # ReportStream utility to handle breaking the streamed results into lines
  # for easy processing. If you'd rather handle processing yourself, you can
  # use download_report_as_stream_with_awql, which just passes data to the
  # block as it's downloaded, without breaking it up into meaningful chunks.
  report_utils.get_stream_helper_with_awql(
        report_query, 'CSV').each_line do |line|
    process_line(line, ad_network_map)
  end

  puts 'Total impressions by ad network type 1:'
  ad_network_map.each do |ad_network_type, total_impressions|
    puts '  %s: %s' % [ad_network_type, total_impressions]
  end
end

def process_line(line, ad_network_map)
  id, ad_network_type_1, impressions = line.split(',')
  ad_network_map[ad_network_type_1] += impressions.to_i
end

if __FILE__ == $0
  API_VERSION = :v201809

  begin
    # File name to write report to.
    stream_criteria_report_results()

  # 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::ReportError => e
    puts "Reporting Error: %s" % e.message
  end
end