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

Remarketing Samples

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

Create a remarketing user list (audience)

#!/usr/bin/perl -w
#
# Copyright 2017, Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This example adds a remarketing user list (a.k.a. Audience) and shows its
# associated conversion tracker code snippet.

use strict;
use lib "../../../lib";
use utf8;

use Google::Ads::AdWords::Client;
use Google::Ads::AdWords::Logging;
use Google::Ads::AdWords::v201809::Predicate;
use Google::Ads::AdWords::v201809::BasicUserList;
use Google::Ads::AdWords::v201809::Selector;
use Google::Ads::AdWords::v201809::UserListConversionType;
use Google::Ads::AdWords::v201809::UserListOperation;

use Cwd qw(abs_path);
use Data::Uniqid qw(uniqid);

# Example main subroutine.
sub add_audience {
  my $client = shift;

  # Create conversion type (tag).
  my $name = "Mars cruise customers #" . uniqid();

  my $conversion_type =
    Google::Ads::AdWords::v201809::UserListConversionType->new({name => $name});

  # Create remarketing user list.
  my $user_list = Google::Ads::AdWords::v201809::BasicUserList->new({
      name            => $name,
      conversionTypes => [$conversion_type],
      # Additional properties (non-required).
      description        => "A list of mars cruise customers in the last year",
      membershipLifeSpan => 365,
      status             => "OPEN"
  });

  # Create operation.
  my $operation = Google::Ads::AdWords::v201809::UserListOperation->new({
      operator => "ADD",
      operand  => $user_list
  });

  # Add user list.
  my $result =
    $client->AdwordsUserListService()->mutate({operations => [$operation]});

  if ($result->get_value()) {
    my $user_list = $result->get_value()->[0];

    # Due to a bug in the service we need to retrieve the conversion trackers
    # info in a separate request after a few seconds.
    sleep(5);
    my $predicate = Google::Ads::AdWords::v201809::Predicate->new({
        field    => "Id",
        operator => "IN",
        values   => [$user_list->get_id()->get_value()]});
    my $selector = Google::Ads::AdWords::v201809::Selector->new({
        fields     => ["ConversionTypes"],
        predicates => [$predicate]});
    my $conversion_trackers_page =
      $client->AdwordsUserListService()->get({serviceSelector => $selector});

    # Get associated conversion snippets.
    my $conversion_id =
      $conversion_trackers_page->get_entries()->[0]->get_conversionTypes()->[0]
      ->get_id()->get_value();

    # Create selector.
    my $conversion_type_predicate =
      Google::Ads::AdWords::v201809::Predicate->new({
        field    => "Id",
        operator => "IN",
        values   => [$conversion_id]});
    $selector = Google::Ads::AdWords::v201809::Selector->new({
        fields     => ["Id", "GoogleGlobalSiteTag", "GoogleEventSnippet"],
        predicates => [$conversion_type_predicate]});

    # Get all conversion trackers.
    my $page =
      $client->ConversionTrackerService()->get({serviceSelector => $selector});

    my $conversion_tracker = $page->get_entries()->[0];

    # Display results.
    printf "User list with name \"%s\" and id \"%d\" was added.\n",
      $user_list->get_name(), $user_list->get_id();
    printf("Google global site tag:\n%s\n\n",
      $conversion_tracker->get_googleGlobalSiteTag());
    printf("Google event snippet:\n%s\n\n",
      $conversion_tracker->get_googleEventSnippet());
  } else {
    print "No user list was added.";
  }

  return 1;
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Log SOAP XML request, response and API errors.
Google::Ads::AdWords::Logging::enable_all_logging();

# Get AdWords Client, credentials will be read from ~/adwords.properties.
my $client = Google::Ads::AdWords::Client->new({version => "v201809"});

# By default examples are set to die on any server returned fault.
$client->set_die_on_faults(1);

# Call the example
add_audience($client);

Create an AdWords conversion tracker and add to it upload conversions

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

use strict;
use lib "../../../lib";
use utf8;

use Google::Ads::AdWords::Client;
use Google::Ads::AdWords::Logging;
use Google::Ads::AdWords::v201809::AdWordsConversionTracker;
use Google::Ads::AdWords::v201809::ConversionTrackerOperation;
use Google::Ads::AdWords::v201809::UploadConversion;

use Cwd qw(abs_path);
use Data::Uniqid qw(uniqid);

# Example main subroutine.
sub add_conversion_trackers {
  my $client = shift;

  my @conversion_trackers = ();

  # Create AdWords conversion tracker.
  my $adwords_conversion_tracker =
    Google::Ads::AdWords::v201809::AdWordsConversionTracker->new({
      name => "Earth to Mars Cruises Conversion #" . uniqid(),
      # Additional properties (non-required).
      status                               => "ENABLED",
      category                             => "DEFAULT",
      viewthroughLookbackWindow            => 15,
      defaultRevenueValue                  => 23.41,
      alwaysUseDefaultRevenueValue         => 1
    });
  push @conversion_trackers, $adwords_conversion_tracker;

  # Create an upload conversion for offline conversion imports.
  my $upload_conversion_tracker =
    Google::Ads::AdWords::v201809::UploadConversion->new({
      # Set an appropriate category. This field is optional, and will be set to
      # DEFAULT if not mentioned.
      category                             => "LEAD",
      name                                 => "Upload Conversion #" . uniqid(),
      viewthroughLookbackWindow            => 30,
      ctcLookbackWindow                    => 90,
      # Optional: Set the default currency code to use for conversions
      # that do not specify a conversion currency. This must be an ISO 4217
      # 3-character currency code such as "EUR" or "USD".
      # If this field is not set on this UploadConversion, AdWords will use
      # the account's currency.
      defaultRevenueCurrencyCode => "EUR",
      # Optional: Set the default revenue value to use for conversions
      # that do not specify a conversion value. Note that this value
      # should NOT be in micros.
      defaultRevenueValue => 2.50,
      # Optional: To upload fractional conversion credits, mark the upload conversion
      # as externally attributed. See
      # https://developers.google.com/adwords/api/docs/guides/conversion-tracking#importing_externally_attributed_conversions
      # to learn more about importing externally attributed conversions.
      # isExternallyAttributed => true
    });
  push @conversion_trackers, $upload_conversion_tracker;

  my @operations = ();
  for my $conversion_tracker (@conversion_trackers) {
    # Create operation.
    my $conversion_operation =
      Google::Ads::AdWords::v201809::ConversionTrackerOperation->new({
        operator => "ADD",
        operand  => $conversion_tracker
      });
    push @operations, $conversion_operation;
  }

  # Add conversion trackers.
  my $result =
    $client->ConversionTrackerService()
    ->mutate({operations => \@operations});

  # Display conversion tracker.
  if ($result->get_value()) {
    foreach my $conversion_tracker (@{$result->get_value()}) {
      printf "Conversion tracker with ID %d, name \"%s\", status \"%s\" " .
        "and category \"%s\" was added.\n", $conversion_tracker->get_id(),
        $conversion_tracker->get_name(), $conversion_tracker->get_status(),
        $conversion_tracker->get_category();
      if ($conversion_tracker
        ->isa("Google::Ads::AdWords::v201809::AdWordsConversionTracker")) {
        printf("Google global site tag:\n%s\n\n",
          $conversion_tracker->get_googleGlobalSiteTag());
        printf("Google event snippet:\n%s\n\n",
          $conversion_tracker->get_googleEventSnippet());
      }
    }
  } else {
    print "No conversion tracker was added.";
  }

  return 1;
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Log SOAP XML request, response and API errors.
Google::Ads::AdWords::Logging::enable_all_logging();

# Get AdWords Client, credentials will be read from ~/adwords.properties.
my $client = Google::Ads::AdWords::Client->new({version => "v201809"});

# By default examples are set to die on any server returned fault.
$client->set_die_on_faults(1);

# Call the example
add_conversion_trackers($client);

Create and populate a user list

#!/usr/bin/perl -w
#
# Copyright 2017, Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This example adds a user list (a.k.a. audience) and uploads members
# to populate the list.
#
# Note: It may take up to several hours for the list to be populated
# with members. Email addresses must be associated with a Google account.
# For privacy purposes, the user list size will show as zero until the list
# has at least 1000 members. After that, the size will be rounded to the
# two most significant digits.

use strict;
use lib "../../../lib";
use utf8;

use Google::Ads::AdWords::Client;
use Google::Ads::AdWords::Logging;
use Google::Ads::AdWords::v201809::AddressInfo;
use Google::Ads::AdWords::v201809::CrmBasedUserList;
use Google::Ads::AdWords::v201809::Member;
use Google::Ads::AdWords::v201809::MutateMembersOperand;
use Google::Ads::AdWords::v201809::MutateMembersOperation;
use Google::Ads::AdWords::v201809::UserListOperation;

use Cwd qw(abs_path);
use Data::Uniqid qw(uniqid);
use Digest::SHA qw(sha256_hex);

sub add_crm_based_user_list {
  my $client = shift;

  # Create a user list.
  my $user_list = Google::Ads::AdWords::v201809::CrmBasedUserList->new({
    name        => "Customer relationship management list #" . uniqid(),
    description => "A list of customers that originated from email addresses",
    # CRM-based user lists can use a membershipLifeSpan of 10000 to indicate
    # unlimited; otherwise normal values apply.
    membershipLifeSpan => "30",
    uploadKeyType => "CONTACT_INFO"
  });

  # Create operation.
  my $operation = Google::Ads::AdWords::v201809::UserListOperation->new({
    operator => "ADD",
    operand  => $user_list
  });

  # Add user list.
  my $result =
    $client->AdwordsUserListService()->mutate({operations => [$operation]});

  # Display user list.
  if ($result->get_value()) {
    my $user_list_added = $result->get_value()->[0];
    printf(
      "User list with name '%s' and ID '%d' was added.\n",
      $user_list_added->get_name(),
      $user_list_added->get_id());
  }

  my @members = ();
  # Add e-mails to the user list.
  if ($result->get_value()) {
    # Use SHA256 to encode the e-mails.
    # All e-mails MUST be trimmed and lower-cased before encoding.
    my @email_list = (
      'customer1@example.com', 'customer2@example.com',
      ' Customer3@example.com '
    );
    foreach my $email (@email_list) {
      $email = sha256_hex(_to_normalized_string($email));
      my $member = Google::Ads::AdWords::v201809::Member->new({
        hashedEmail => $email
      });
      push @members, $member;
    }

    my $first_name   = "John";
    my $last_name    = "Doe";
    my $country_code = "US";
    my $zip_code     = "10011";

    my $address_info = Google::Ads::AdWords::v201809::AddressInfo->new({
      # First and last name must be normalized and hashed.
      hashedFirstName => sha256_hex(_to_normalized_string($first_name)),
      hashedLastName  => sha256_hex(_to_normalized_string($last_name)),
      # Country code and zip code are sent in plaintext.
      countryCode => $country_code,
      zipCode     => $zip_code
    });

    my $member_by_address = Google::Ads::AdWords::v201809::Member->new({
      addressInfo => $address_info
    });
    push @members, $member_by_address;

    my $userlist_id = $result->get_value()->[0]->get_id();
    my $operand     = Google::Ads::AdWords::v201809::MutateMembersOperand->new({
      userListId => $userlist_id,
      membersList => \@members
    });

    my $member_operation =
      Google::Ads::AdWords::v201809::MutateMembersOperation->new({
        operator => "ADD",
        operand  => $operand
      });

    # Add members to the user list based on email addresses.
    my $result =
      $client->AdwordsUserListService()
      ->mutateMembers({operations => [$member_operation]});

    # Display results.
    # Reminder: it may take up to 9 hours for the list to be populated
    # with members.
    for $user_list ($result->get_userLists()) {
      printf(
        "%d email addresses were uploaded to user list with name '%s' " .
          "and ID '%d' and are scheduled for review.\n",
        scalar(@email_list), $user_list->get_name(),
        $user_list->get_id());
    }

  } else {
    print "No user list was added.";
  }

  return 1;
}

sub _to_normalized_string {
  my ($input_string) = @_;
  $input_string =~ s/^\s+|\s+$//g;
  $input_string = lc $input_string;
  return $input_string;
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Log SOAP XML request, response and API errors.
Google::Ads::AdWords::Logging::enable_all_logging();

# Get AdWords Client, credentials will be read from ~/adwords.properties.
my $client = Google::Ads::AdWords::Client->new({version => "v201809"});

# By default examples are set to die on any server returned fault.
$client->set_die_on_faults(1);

# Call the example
add_crm_based_user_list($client);

Create rule-based user lists

#!/usr/bin/perl -w
#
# Copyright 2017, Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This example adds two rule-based remarketing user lists: one with no site
# visit date restrictions, and another that will only include users who visit
# your site in the next six months.

use strict;
use lib "../../../lib";
use utf8;

use Google::Ads::AdWords::Client;
use Google::Ads::AdWords::Logging;
use Google::Ads::AdWords::v201809::BasicUserList;
use Google::Ads::AdWords::v201809::CombinedRuleUserList;
use Google::Ads::AdWords::v201809::DateKey;
use Google::Ads::AdWords::v201809::DateRuleItem;
use Google::Ads::AdWords::v201809::DateSpecificRuleUserList;
use Google::Ads::AdWords::v201809::ExpressionRuleUserList;
use Google::Ads::AdWords::v201809::NumberKey;
use Google::Ads::AdWords::v201809::NumberRuleItem;
use Google::Ads::AdWords::v201809::Operator;
use Google::Ads::AdWords::v201809::Rule;
use Google::Ads::AdWords::v201809::RuleItem;
use Google::Ads::AdWords::v201809::RuleItemGroup;
use Google::Ads::AdWords::v201809::StringKey;
use Google::Ads::AdWords::v201809::StringRuleItem;
use Google::Ads::AdWords::v201809::UserListOperation;

use Cwd qw(abs_path);
use Data::Uniqid qw(uniqid);

# Example main subroutine.
sub add_rule_based_user_lists {
  my $client = shift;

  # First rule item group - users who visited the checkout page and had more
  # than one item in their shopping cart.
  my $checkout_string_rule_item =
    Google::Ads::AdWords::v201809::StringRuleItem->new({
      key => Google::Ads::AdWords::v201809::StringKey->new(
        {name => "ecomm_pagetype"}
      ),
      op    => "EQUALS",
      value => "checkout"
    });
  my $checkout_rule_item = Google::Ads::AdWords::v201809::RuleItem->new(
    {StringRuleItem => $checkout_string_rule_item});

  my $cart_size_number_rule_item =
    Google::Ads::AdWords::v201809::NumberRuleItem->new({
      key =>
        Google::Ads::AdWords::v201809::NumberKey->new({name => "cartsize"}),
      op    => "GREATER_THAN",
      value => "1"
    });
  my $cart_size_rule_item = Google::Ads::AdWords::v201809::RuleItem->new(
    {NumberRuleItem => $cart_size_number_rule_item});

  # Combine the two rule items into a RuleItemGroup so AdWords will AND their
  # rules together.
  my $checkout_multiple_item_group =
    Google::Ads::AdWords::v201809::RuleItemGroup->new(
    {items => [$checkout_rule_item, $cart_size_rule_item]});

  # Second rule item group - users who check out during the next three months.
  my ($sec, $min, $hour, $mday, $mon, $year) = localtime();
  my $rule_start_date =
    sprintf("%d%02d%02d", ($year + 1900), ($mon + 1), $mday);
  ($sec, $min, $hour, $mday, $mon, $year) =
    localtime(time + 60 * 60 * 24 * 30 * 3);
  my $rule_end_date = sprintf("%d%02d%02d", ($year + 1900), ($mon + 1), $mday);

  my $start_date_date_rule_item =
    Google::Ads::AdWords::v201809::DateRuleItem->new({
      key =>
        Google::Ads::AdWords::v201809::DateKey->new({name => "checkoutdate"}),
      op    => "AFTER",
      value => $rule_start_date
    });

  my $start_date_rule_item = Google::Ads::AdWords::v201809::RuleItem->new(
    {DateRuleItem => $start_date_date_rule_item});

  my $end_date_date_rule_item =
    Google::Ads::AdWords::v201809::DateRuleItem->new({
      key =>
        Google::Ads::AdWords::v201809::DateKey->new({name => "checkoutdate"}),
      op    => "BEFORE",
      value => $rule_end_date
    });

  my $end_date_rule_item = Google::Ads::AdWords::v201809::RuleItem->new(
    {DateRuleItem => $end_date_date_rule_item});

  # Combine the date rule items into a RuleItemGroup.
  my $checked_out_next_three_months_item_group =
    Google::Ads::AdWords::v201809::RuleItemGroup->new(
    {items => [$start_date_rule_item, $end_date_rule_item]});

  # Combine the rule item groups into a Rule so AdWords knows how to apply the
  # rules.
  my $rule = Google::Ads::AdWords::v201809::Rule->new({
      groups => [
        $checkout_multiple_item_group, $checked_out_next_three_months_item_group
      ],
      # ExpressionRuleUserLists can use either CNF or DNF for matching. CNF
      # means 'at least one item in each rule item group must match', and DNF
      # means 'at least one entire rule item group must match'.
      # DateSpecificRuleUserList only supports DNF. You can also omit the rule
      # type altogether to default to DNF.
      ruleType => 'DNF'
    });

  # Third and fourth rule item groups
  # Visitors of a page who visited another page.
  my $site1_string_rule_item =
    Google::Ads::AdWords::v201809::StringRuleItem->new({
      key => Google::Ads::AdWords::v201809::StringKey->new(
        {name => "url__"}
      ),
      op    => "EQUALS",
      value => "example.com/example1"
    });
  my $site1_rule_item = Google::Ads::AdWords::v201809::RuleItem->new(
    {StringRuleItem => $site1_string_rule_item});
  my $site2_string_rule_item =
    Google::Ads::AdWords::v201809::StringRuleItem->new({
      key => Google::Ads::AdWords::v201809::StringKey->new(
        {name => "url__"}
      ),
      op    => "EQUALS",
      value => "example.com/example2"
    });
  my $site2_rule_item = Google::Ads::AdWords::v201809::RuleItem->new(
    {StringRuleItem => $site2_string_rule_item});

  # Create two RuleItemGroups to show that a visitor browsed two sites.
  my $site1_item_group = Google::Ads::AdWords::v201809::RuleItemGroup->new(
    {items => [$site1_rule_item]});
  my $site2_item_group = Google::Ads::AdWords::v201809::RuleItemGroup->new(
    {items => [$site2_rule_item]});

  # Create two rules to show that a visitor browsed two sites.
  my $user_visited_site1_rule = Google::Ads::AdWords::v201809::Rule->new({
      groups => [$site1_item_group]
    });
  my $user_visited_site2_rule = Google::Ads::AdWords::v201809::Rule->new({
      groups => [$site2_item_group]
    });

  # Create the user list with no restrictions on site visit date.
  ($sec, $min, $hour, $mday, $mon, $year) = localtime();
  my $creation_time = sprintf(
    "%d%02d%02d_%02d%02d%02d",
    ($year + 1900),
    ($mon + 1),
    $mday, $hour, $min, $sec
  );
  my $expression_user_list =
    Google::Ads::AdWords::v201809::ExpressionRuleUserList->new({
      name        => "Expression based user list created at ${creation_time}",
      description => "Users who checked out in six month window OR " .
        "visited the checkout page with more than one item in their cart",
      rule => $rule,
      # Optional: Set the prepopulationStatus to REQUESTED to include past
      # users in the user list.
      prepopulationStatus => "REQUESTED"
    });

  # Create the user list restricted to users who visit your site within the
  # next six months.
  ($sec, $min, $hour, $mday, $mon, $year) = localtime();
  my $list_start_date =
    sprintf("%d%02d%02d", ($year + 1900), ($mon + 1), $mday);
  ($sec, $min, $hour, $mday, $mon, $year) =
    localtime(time + 60 * 60 * 24 * 30 * 6);
  my $list_end_date = sprintf("%d%02d%02d", ($year + 1900), ($mon + 1), $mday);

  my $date_user_list =
    Google::Ads::AdWords::v201809::DateSpecificRuleUserList->new({
      name        => "Date rule user list created at ${creation_time}",
      description => "Users who visited the site between " .
        "${list_start_date} and ${list_end_date} and checked out " .
        "in three month window OR visited the checkout page with " .
        "more than one item in their cart.",
      rule      => $rule,
      startDate => $list_start_date,
      endDate   => $list_end_date
    });

  # Create the user list where "Visitors of a page who did visit another page".
  # To create a user list where "Visitors of a page who did not visit another
  # page", change the ruleOperator from AND to AND_NOT.
  my $combined_user_list =
    Google::Ads::AdWords::v201809::CombinedRuleUserList->new({
      name         => "Combined rule user list created at ${creation_time}",
      description  => "Users who visited two sites.",
      leftOperand  => $user_visited_site1_rule,
      rightOperand => $user_visited_site2_rule,
      ruleOperator => "AND"
    });

  # Create operations to add the user lists.
  my $operations = [
    Google::Ads::AdWords::v201809::UserListOperation->new({
        operator => "ADD",
        operand  => $expression_user_list
      }
    ),
    Google::Ads::AdWords::v201809::UserListOperation->new({
        operator => "ADD",
        operand  => $date_user_list
      }
    ),
    Google::Ads::AdWords::v201809::UserListOperation->new({
        operator => "ADD",
        operand  => $combined_user_list
      })];

  # Submit the operations.
  my $result =
    $client->AdwordsUserListService()->mutate({operations => $operations});

  if ($result->get_value()) {
    foreach my $user_list (@{$result->get_value}) {
      printf "User list added with ID %d, name '%s', status '%s', list " .
        "type '%s', accountUserListStatus '%s', description '%s'.\n",
        $user_list->get_id(),     $user_list->get_name(),
        $user_list->get_status(), $user_list->get_listType(),
        $user_list->get_accountUserListStatus(),
        $user_list->get_description();
    }
  } else {
    print "No user list was added.";
  }

  return 1;
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Log SOAP XML request, response and API errors.
Google::Ads::AdWords::Logging::enable_all_logging();

# Get AdWords Client, credentials will be read from ~/adwords.properties.
my $client = Google::Ads::AdWords::Client->new({version => "v201809"});

# By default examples are set to die on any server returned fault.
$client->set_die_on_faults(1);

# Call the example
add_rule_based_user_lists($client);

Import conversion adjustments for existing conversions

#!/usr/bin/perl -w
#
# Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#        https://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 imports conversion adjustments for conversions that already
# exist. To set up a conversion tracker, run the add_conversion_tracker.pl
# example.

use strict;
use lib "../../../lib";
use utf8;

use Google::Ads::AdWords::Client;
use Google::Ads::AdWords::Logging;
use Google::Ads::AdWords::v201809::GclidOfflineConversionAdjustmentFeed;
use Google::Ads::AdWords::v201809::OfflineConversionAdjustmentFeedOperation;

use Cwd qw(abs_path);
use Data::Uniqid qw(uniqid);

# Replace with valid values of your account.
my $conversion_name     = "INSERT_CONVERSION_NAME_HERE";
my $gclid               = "INSERT_GCLID_HERE";
my $adjustment_type     = "INSERT_ADJUSTMENT_TYPE_HERE";
my $conversion_time     = "INSERT_CONVERSION_TIME_HERE";
my $adjustment_time     = "INSERT_ADJUSTMENT_TIME_HERE";
my $adjusted_value      = "INSERT_ADJUST_VALUE_HERE";

# Example main subroutine.
sub upload_conversion_adjustment {
  my ($client, $conversion_name, $gclid, $adjustment_type,
      $conversion_time, $adjustment_time, $adjusted_value) = @_;
  # This example demonstrates adjusting one conversion, but you can add more
  # than one operation to adjust more in a single mutate request.
  my @conversion_adjustment_operations = ();

  # Associate conversion adjustments with the existing named conversion
  # tracker. The GCLID should have been uploaded before with a conversion.
  my $feed =
      Google::Ads::AdWords::v201809::GclidOfflineConversionAdjustmentFeed->new({
      conversionName  => $conversion_name,
      adjustmentType  => $adjustment_type,
      conversionTime  => $conversion_time,
      adjustmentTime  => $adjustment_time,
      adjustedValue   => $adjusted_value,
      googleClickId   => $gclid,
  });

  my $conversion_adjustment_operation =
    Google::Ads::AdWords::v201809::OfflineConversionAdjustmentFeedOperation->new({
      operator => "ADD",
      operand  => $feed
    });

  push @conversion_adjustment_operations, $conversion_adjustment_operation;

  # Add the conversion adjustment.
  my $result =
    $client->OfflineConversionAdjustmentFeedService()
    ->mutate({operations => \@conversion_adjustment_operations});

  # Display results.
  if ($result->get_value()) {
    foreach my $adjustment_feed (@{$result->get_value()}) {
      printf "Uploaded conversion adjusted value of \"%s\" for Google Click " .
        "ID \"%s\"\n",
        $adjustment_feed->get_adjustedValue(),
        $adjustment_feed->get_googleClickId();
    }
  } else {
    print "No conversion adjustments were added.\n";
    return;
  }

  return 1;
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Log SOAP XML request, response and API errors.
Google::Ads::AdWords::Logging::enable_all_logging();

# Get AdWords Client, credentials will be read from ~/adwords.properties.
my $client = Google::Ads::AdWords::Client->new({version => "v201809"});

# By default examples are set to die on any server returned fault.
$client->set_die_on_faults(1);

# Call the example
upload_conversion_adjustment($client, $conversion_name, $gclid,
    $adjustment_type, $conversion_time, $adjustment_time, $adjusted_value);

Import offline call conversions

#!/usr/bin/perl -w
#
# Copyright 2017, Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This code example imports offline call conversion values for calls
# related to the ads in your account. To set up a conversion tracker, run the
# add_conversion_tracker.pl example using UploadCallConversion as the
# conversion tracker.

use strict;
use lib "../../../lib";
use utf8;

use Google::Ads::AdWords::Client;
use Google::Ads::AdWords::Logging;
use Google::Ads::AdWords::v201809::OfflineCallConversionFeed;
use Google::Ads::AdWords::v201809::OfflineCallConversionFeedOperation;

use Cwd qw(abs_path);
use Data::Uniqid qw(uniqid);

# Replace with valid values of your account.
my $caller_id        = "INSERT_CALLER_ID_HERE";
# For times use the format yyyyMMdd HHmmss tz. For more details on formats,
# see: https://developers.google.com/adwords/api/docs/appendix/codes-formats#date-and-time-formats
# For time zones, see: https://developers.google.com/adwords/api/docs/appendix/codes-formats#timezone-ids
my $call_start_time  = "INSERT_CALL_START_TIME_HERE";
my $conversion_name  = "INSERT_CONVERSION_NAME_HERE";
my $conversion_time  = "INSERT_CONVERSION_TIME_HERE";
my $conversion_value = "INSERT_CONVERSION_VALUE_HERE";

# Example main subroutine.
sub upload_offline_call_conversions {
  my $client                             = shift;
  my $caller_id                          = shift;
  my $call_start_time                    = shift;
  my $conversion_name                    = shift;
  my $conversion_time                    = shift;
  my $conversion_value                   = shift;
  my @offline_call_conversion_operations = ();

  # Associate offline call conversions with the existing named
  # conversion tracker. If this tracker was newly created, it may be a
  # few hours before it can accept conversions.
  my $feed = Google::Ads::AdWords::v201809::OfflineCallConversionFeed->new({
    callerId        => $caller_id,
    callStartTime   => $call_start_time,
    conversionName  => $conversion_name,
    conversionTime  => $conversion_time,
    conversionValue => $conversion_value
  });

  my $offline_call_conversion_operation =
    Google::Ads::AdWords::v201809::OfflineCallConversionFeedOperation->new({
      operator => "ADD",
      operand  => $feed
    });

  push @offline_call_conversion_operations, $offline_call_conversion_operation;

  # This example uploads only one call conversion, but you can upload multiple
  # call conversions by passing additional operations.
  my $result =
    $client->OfflineCallConversionFeedService()
    ->mutate({operations => \@offline_call_conversion_operations});

  # Display results.
  if ($result->get_value()) {
    foreach my $feed_result (@{$result->get_value()}) {
      printf "Uploaded offline call conversion value of \"%s\" for caller ID " .
        "\"%s\".\n",
        $feed_result->get_conversionValue(),
        $feed_result->get_callerId();
    }
  } else {
    print "No offline call conversions were added.\n";
    return;
  }

  return 1;
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Log SOAP XML request, response and API errors.
Google::Ads::AdWords::Logging::enable_all_logging();

# Get AdWords Client, credentials will be read from ~/adwords.properties.
my $client = Google::Ads::AdWords::Client->new({version => "v201809"});

# By default examples are set to die on any server returned fault.
$client->set_die_on_faults(1);

# Call the example
upload_offline_call_conversions($client, $caller_id, $call_start_time,
  $conversion_name, $conversion_time, $conversion_value);

Import offline click conversions

#!/usr/bin/perl -w
#
# Copyright 2017, Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This code example imports offline call conversion values for calls to
# your account. To get Google Click ID for a click, run
# CLICK_PERFORMANCE_REPORT. To set up a conversion tracker, run the
# add_conversion_tracker.pl example.

use strict;
use lib "../../../lib";
use utf8;

use Google::Ads::AdWords::Client;
use Google::Ads::AdWords::Logging;
use Google::Ads::AdWords::v201809::UploadConversion;
use Google::Ads::AdWords::v201809::ConversionTrackerOperation;
use Google::Ads::AdWords::v201809::OfflineCallConversionFeed;
use Google::Ads::AdWords::v201809::OfflineCallConversionFeedOperation;

use Cwd qw(abs_path);
use Data::Uniqid qw(uniqid);

# Replace with valid values of your account.
my $conversion_name  = "INSERT_CONVERSION_NAME_HERE";
my $gclid            = "INSERT_GCLID_HERE";
my $conversion_time  = "INSERT_CONVERSION_TIME_HERE";
my $conversion_value = "INSERT_CONVERSION_VALUE_HERE";

# Example main subroutine.
sub upload_offline_conversions {
  my $client                        = shift;
  my $conversion_name               = shift;
  my $gclid                         = shift;
  my $conversion_time               = shift;
  my $conversion_value              = shift;
  my @offline_conversion_operations = ();

  # Associate offline conversions with the existing named conversion tracker. If
  # this tracker was newly created, it may be a few hours before it can accept
  # conversions.
  my $feed = Google::Ads::AdWords::v201809::OfflineCallConversionFeed->new({
      conversionName  => $conversion_name,
      conversionTime  => $conversion_time,
      conversionValue => $conversion_value,
      googleClickId   => $gclid
  });

  my $offline_conversion_operation =
    Google::Ads::AdWords::v201809::OfflineCallConversionFeedOperation->new({
      operator => "ADD",
      operand  => $feed
    });

  push @offline_conversion_operations, $offline_conversion_operation;

  # Add the upload conversion.
  my $feed_result =
    $client->OfflineCallConversionFeedService()
    ->mutate({operations => \@offline_conversion_operations});

  # Display results.
  if ($feed_result->get_value()) {
    foreach my $oc_feed (@{$feed_result->get_value()}) {
      printf "Uploaded offline conversion value of \"%s\" for Google Click " .
        "ID \"%s\" was created.\n",
        $oc_feed->get_conversionName(),
        $oc_feed->get_googleClickId();
    }
  } else {
    print "No offline conversion were added.\n";
    return;
  }

  return 1;
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Log SOAP XML request, response and API errors.
Google::Ads::AdWords::Logging::enable_all_logging();

# Get AdWords Client, credentials will be read from ~/adwords.properties.
my $client = Google::Ads::AdWords::Client->new({version => "v201809"});

# By default examples are set to die on any server returned fault.
$client->set_die_on_faults(1);

# Call the example
upload_offline_conversions($client, $conversion_name, $gclid, $conversion_time,
  $conversion_value);

Upload offline data for store sales transactions

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

use strict;
use lib "../../../lib";
use utf8;

use Google::Ads::AdWords::Client;
use Google::Ads::AdWords::Logging;
use Google::Ads::AdWords::v201809::ConversionTrackerOperation;
use Google::Ads::AdWords::v201809::FirstPartyUploadMetadata;
use Google::Ads::AdWords::v201809::Money;
use Google::Ads::AdWords::v201809::MoneyWithCurrency;
use Google::Ads::AdWords::v201809::OfflineCallConversionFeed;
use Google::Ads::AdWords::v201809::OfflineCallConversionFeedOperation;
use Google::Ads::AdWords::v201809::OfflineData;
use Google::Ads::AdWords::v201809::OfflineDataUploadOperation;
use Google::Ads::AdWords::v201809::StoreSalesTransaction;
use Google::Ads::AdWords::v201809::ThirdPartyUploadMetadata;
use Google::Ads::AdWords::v201809::UploadConversion;
use Google::Ads::AdWords::v201809::UploadMetadata;
use Google::Ads::AdWords::v201809::UserIdentifier;

use Cwd qw(abs_path);
use Data::Uniqid qw(uniqid);
use Digest::SHA qw(sha256_hex);

# Replace with valid values of your account.
# The external upload ID can be any number that you use to keep track of your
# uploads.
my $external_upload_id = 'INSERT_EXTERNAL_UPLOAD_ID';
# Insert the conversion type name that you'd like to attribute this upload to.
my $conversion_name = 'INSERT_CONVERSION_NAME';

# Change the below constant to ThirdPartyUploadMetadata if uploading third
# party data.
my $store_sales_upload_common_metadata_type = "FirstPartyUploadMetadata";
# Insert email addresses below for creating user identifiers.
my @email_addresses = ("EMAIL_ADDRESS_1", "EMAIL_ADDRESS_2");
# The three constants below are needed when uploading third party data.
# You can safely ignore them if you are uploading first party data.
# For times, use the format yyyyMMdd HHmmss tz. For more details on formats,
# see:
# https://developers.google.com/adwords/api/docs/appendix/codes-formats#date-and-time-formats
# For time zones, see:
# https://developers.google.com/adwords/api/docs/appendix/codes-formats#timezone-ids
my $advertiser_upload_time = "INSERT_ADVERTISER_UPLOAD_TIME";
my $bridge_map_version_id  = "INSERT_BRIDGE_MAP_VERSION_ID";
my $partner_id             = "INSERT_PARTNER_ID";

# Example main subroutine.
sub upload_offline_data {
  my (
    $client,                $external_upload_id,
    $conversion_name,       $store_sales_upload_common_metadata_type,
    $email_addresses,       $advertiser_upload_time,
    $bridge_map_version_id, $partner_id
  ) = @_;

  # Create the first offline data for upload.
  # This transaction occurred 7 days ago with amount of 200 USD.
  my ($second, $minute, $hour, $mday, $mon, $year) =
    localtime(time - 60 * 60 * 24 * 7);
  my $transaction_time_1 = sprintf(
    "%d%02d%02d %02d%02d%02d",
    ($year + 1900),
    ($mon + 1),
    $mday, $hour, $minute, $second
  );
  my $transaction_amount_1        = 200000000;
  my $transaction_currency_code_1 = 'USD';
  my $user_identifier_list_1      = [
    _create_user_identifier('HASHED_EMAIL', $email_addresses->[0]),
    _create_user_identifier('STATE',        'New York')];

  my $offline_data_1 = _create_offline_data(
    $transaction_time_1,          $transaction_amount_1,
    $transaction_currency_code_1, $conversion_name,
    $user_identifier_list_1
  );

  # Create the second offline data for upload.
  # This transaction occurred 14 days ago with amount of 450 EUR.
  ($second, $minute, $hour, $mday, $mon, $year) =
    localtime(time - 60 * 60 * 24 * 14);
  my $transaction_time_2 = sprintf(
    "%d%02d%02d %02d%02d%02d",
    ($year + 1900),
    ($mon + 1),
    $mday, $hour, $minute, $second
  );
  my $transaction_amount_2        = 450000000;
  my $transaction_currency_code_2 = 'EUR';
  my $user_identifier_list_2      = [
    _create_user_identifier('HASHED_EMAIL', $email_addresses->[1]),
    _create_user_identifier('STATE',        'California')];

  my $offline_data_2 = _create_offline_data(
    $transaction_time_2,          $transaction_amount_2,
    $transaction_currency_code_2, $conversion_name,
    $user_identifier_list_2
  );

  # Create offline data upload object.
  my $offline_data_upload = undef;
  if ($store_sales_upload_common_metadata_type eq 'FirstPartyUploadMetadata') {
    $offline_data_upload =
      Google::Ads::AdWords::v201809::OfflineDataUpload->new({
        externalUploadId => $external_upload_id,
        offlineDataList  => [$offline_data_1, $offline_data_2],
        uploadType       => 'STORE_SALES_UPLOAD_FIRST_PARTY',
        uploadMetadata   => Google::Ads::AdWords::v201809::UploadMetadata->new({
            StoreSalesUploadCommonMetadata =>
              Google::Ads::AdWords::v201809::FirstPartyUploadMetadata->new({
                loyaltyRate           => "1.0",
                transactionUploadRate => "1.0",
              })
        })
      });
  } elsif (
    $store_sales_upload_common_metadata_type eq 'ThirdPartyUploadMetadata')
  {
    $offline_data_upload =
      Google::Ads::AdWords::v201809::OfflineDataUpload->new({
        externalUploadId => $external_upload_id,
        offlineDataList  => [$offline_data_1, $offline_data_2],
        # Set the type and metadata of this upload.
        uploadType     => 'STORE_SALES_UPLOAD_THIRD_PARTY',
        uploadMetadata => Google::Ads::AdWords::v201809::UploadMetadata->new({
            StoreSalesUploadCommonMetadata =>
              Google::Ads::AdWords::v201809::ThirdPartyUploadMetadata->new({
                loyaltyRate           => "1.0",
                transactionUploadRate => "1.0",
                advertiserUploadTime  => $advertiser_upload_time,
                validTransactionRate  => "1.0",
                partnerMatchRate      => "1.0",
                partnerUploadRate     => "1.0",
                bridgeMapVersionId    => $bridge_map_version_id,
                partnerId             => $partner_id
              })
        })
      });
  }

  # Create an offline data upload operation.
  my @operations = ();
  my $offline_data_upload_operation =
    Google::Ads::AdWords::v201809::OfflineDataUploadOperation->new({
      operator => 'ADD',
      operand  => $offline_data_upload
    });
  push @operations, $offline_data_upload_operation;

  # Upload offline data on the server and print some information.
  my $result =
    $client->OfflineDataUploadService()->mutate({operations => \@operations});
  $offline_data_upload = $result->get_value()->[0];

  printf(
    "Uploaded offline data with external upload ID %d and upload status" .
      " %s\n",
    $offline_data_upload->get_externalUploadId(),
    $offline_data_upload->get_uploadStatus());

  # Print any partial failure errors from the response.
  if ($result->get_partialFailureErrors()) {
    foreach my $api_error (@{$result->get_partialFailureErrors()}) {
      # Get the index of the failed operation from the error's field path
      # elements.
      my $operation_index =
        _get_field_path_element_index($api_error, "operations");
      if (defined($operation_index)) {
        my $failed_offline_data_upload =
          $operations[$operation_index]->get_operand();
        # Get the index of the entry in the offline data list from the error's
        # field path elements.
        my $offline_data_list_index =
          _get_field_path_element_index($api_error, "offlineDataList");
        printf("Offline data list entry %d in operation %d with external"
          . " upload ID %d and type \"%s\" has triggered a failure for the "
          . " following reason: \"%s\".\n",
          $offline_data_list_index,
          $operation_index,
          $offline_data_upload->get_externalUploadId(),
          $offline_data_upload->get_uploadType(),
          $api_error->get_errorString());
      } else {
        printf "A failure for the following reason: \"%s\" has occurred.\n",
          $api_error->get_errorString();
      }
    }
  }
  return 1;
}

# Returns the FieldPathElement#getIndex() for the specified field name, if
# present in the error's field path elements.
sub _get_field_path_element_index {
  my ($api_error, $field) = @_;

  my $field_path_elements = $api_error->get_fieldPathElements();
  if (!$field_path_elements) {
    return;
  }
  for (my $i = 0 ; $i < scalar $field_path_elements; $i++) {
    my $field_path_element = $field_path_elements->[$i];
    if ($field eq $field_path_element->get_field()) {
      return $field_path_element->get_index();
    }
  }
  return;
}

# Creates the offline data from the specified transaction time, transaction
# micro amount, transaction currency, conversion name and user identifier
# list.
sub _create_offline_data {
  my (
    $transaction_time,     $transaction_micro_amount,
    $transaction_currency, $conversion_name,
    $user_identifier_list
  ) = @_;
  my $store_sales_transaction =
    Google::Ads::AdWords::v201809::StoreSalesTransaction->new({
      conversionName => $conversion_name,
      # For times use the format yyyyMMdd HHmmss [tz].
      # For details, see
      # https://developers.google.com/adwords/api/docs/appendix/codes-formats#date-and-time-formats
      transactionTime => $transaction_time,
      userIdentifiers => $user_identifier_list,
      transactionAmount =>
        Google::Ads::AdWords::v201809::MoneyWithCurrency->new({
          currencyCode => $transaction_currency,
          money        => Google::Ads::AdWords::v201809::Money->new(
            {microAmount => $transaction_micro_amount})})});
  # There is a temporary issue with the WSDL in which there is a
  # namespace conflict. As a workaround, the namespace is explicitly set here.
  $Google::Ads::AdWords::v201809::MoneyWithCurrency::OBJECT_NAMESPACE =
    "https://adwords.google.com/api/adwords/rm/v201809";

  my $offline_data = Google::Ads::AdWords::v201809::OfflineData->new({
    StoreSalesTransaction => $store_sales_transaction
  });
  return $offline_data;
}

# Creates a user identifier from the specified type and value.
sub _create_user_identifier {
  my ($type, $value) = @_;
  # If the user identifier type is a hashed type, also call hash function
  # on the value.
  if ($type =~ /^HASHED_/) {
    $value = sha256_hex($value);
  }

  my $user_identifier = Google::Ads::AdWords::v201809::UserIdentifier->new({
    userIdentifierType => $type,
    value              => $value
  });

  return $user_identifier;
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Log SOAP XML request, response and API errors.
Google::Ads::AdWords::Logging::enable_all_logging();

# Get AdWords Client, credentials will be read from ~/adwords.properties.
my $client = Google::Ads::AdWords::Client->new({version => "v201809"});

# By default examples are set to die on any server returned fault.
$client->set_die_on_faults(0);

# Setting partial failures to true as this example show how to handle them.
$client->set_partial_failure(1);

# Call the example
upload_offline_data(
  $client,                $external_upload_id,
  $conversion_name,       $store_sales_upload_common_metadata_type,
  \@email_addresses,      $advertiser_upload_time,
  $bridge_map_version_id, $partner_id
);