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/env python
#
# Copyright 2016 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""This example adds a remarketing user list (a.k.a. audience).

The LoadFromStorage method is pulling credentials and properties from a
"googleads.yaml" file. By default, it looks for this file in your home
directory. For more information, see the "Caching authentication information"
section of our README.

"""


import uuid
from googleads import adwords


PAGE_SIZE = 100


def main(client):
  # Initialize appropriate service.
  user_list_service = client.GetService(
      'AdwordsUserListService', version='v201809')
  conversion_tracker_service = client.GetService(
      'ConversionTrackerService', version='v201809')

  # Construct operations and add a user list.
  operations = [
      {
          'operator': 'ADD',
          'operand': {
              'xsi_type': 'BasicUserList',
              'name': 'Mars cruise customers #%s' % uuid.uuid4(),
              'description': 'A list of mars cruise customers in the last year',
              'membershipLifeSpan': '365',
              'conversionTypes': [
                  {
                      'name': ('Mars cruise customers #%s'
                               % uuid.uuid4())
                  }
              ],
              # Optional field.
              'status': 'OPEN',
          }
      }
  ]
  user_list_result = user_list_service.mutate(operations)

  # Capture the ID(s) of the conversion.
  if 'value' in user_list_result:
    conversion_ids = []
    for user_list in user_list_result['value']:
      if user_list['conversionTypes']:
        for conversion_type in user_list['conversionTypes']:
          conversion_ids.append(conversion_type['id'])

    # Create query.
    query = (adwords.ServiceQueryBuilder()
             .Select('Id', 'GoogleGlobalSiteTag', 'GoogleEventSnippet')
             .Where('Id').In(*conversion_ids)
             .Limit(0, PAGE_SIZE)
             .Build())

    conversions_map = {}

    # Get all conversion trackers.
    for page in query.Pager(conversion_tracker_service):
      if 'entries' in page:
        for conversion_tracker in page['entries']:
          conversions_map[conversion_tracker['id']] = conversion_tracker

    for user_list in user_list_result['value']:
      print ('User list with name "%s" and ID "%s" was added.'
             % (user_list['name'], user_list['id']))
      if user_list['conversionTypes']:
        for conversion_type in user_list['conversionTypes']:
          conversion_tracker = conversions_map[conversion_type['id']]
          print ('Google global site tag:\n%s\n\n'
                 % conversion_tracker['googleGlobalSiteTag'])
          print ('Google event snippet:\n%s\n\n'
                 % conversion_tracker['googleEventSnippet'])
  else:
    print 'No user lists were added.'


if __name__ == '__main__':
  # Initialize client object.
  adwords_client = adwords.AdWordsClient.LoadFromStorage()

  main(adwords_client)

Create an AdWords conversion tracker and add to it upload conversions

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

"""This example adds an AdWords conversion.

The LoadFromStorage method is pulling credentials and properties from a
"googleads.yaml" file. By default, it looks for this file in your home
directory. For more information, see the "Caching authentication information"
section of our README.

"""


import uuid
from googleads import adwords


def main(client):
  # Initialize appropriate service.
  conversion_tracker_service = client.GetService(
      'ConversionTrackerService', version='v201809')

  # Construct operations and add conversion_tracker.
  operations = [
      {
          'operator': 'ADD',
          'operand': {
              'xsi_type': 'AdWordsConversionTracker',
              'name': 'Mars cruise customers #%s' % uuid.uuid4(),
              'category': 'DEFAULT',
              # Optional fields.
              'status': 'ENABLED',
              'viewthroughLookbackWindow': '15',
              'defaultRevenueValue': '23.41',
              'alwaysUseDefaultRevenueValue': 'true'
          }
      }
  ]
  conversion_trackers = conversion_tracker_service.mutate(operations)

  # Display results.
  for conversion_tracker in conversion_trackers['value']:
    print ('Conversion tracker with id "%s", name "%s", status "%s" '
           'and category "%s" was added.\n' %
           (conversion_tracker['id'], conversion_tracker['name'],
            conversion_tracker['status'], conversion_tracker['category']))


if __name__ == '__main__':
  # Initialize client object.
  adwords_client = adwords.AdWordsClient.LoadFromStorage()

  main(adwords_client)

Create and populate a user list

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


"""Adds a user list and populates it with hashed email addresses.

Note: It may take 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.
"""


import hashlib
import uuid

# Import appropriate modules from the client library.
from googleads import adwords


def main(client):
  # Initialize appropriate services.
  user_list_service = client.GetService('AdwordsUserListService', 'v201809')

  user_list = {
      'xsi_type': 'CrmBasedUserList',
      'name': 'Customer relationship management list #%d' % uuid.uuid4(),
      '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 an operation to add the user list.
  operations = [{
      'operator': 'ADD',
      'operand': user_list
  }]

  result = user_list_service.mutate(operations)
  user_list_id = result['value'][0]['id']

  emails = ['customer1@example.com', 'customer2@example.com',
            ' Customer3@example.com ']
  members = [{'hashedEmail': NormalizeAndSHA256(email)} for email in emails]

  # Add address info.
  members.append({
      'addressInfo': {
          # First and last name must be normalized and hashed.
          'hashedFirstName': NormalizeAndSHA256('John'),
          'hashedLastName': NormalizeAndSHA256('Doe'),
          # Country code and zip code are sent in plaintext.
          'countryCode': 'US',
          'zipCode': '10001'
      }
  })

  mutate_members_operation = {
      'operand': {
          'userListId': user_list_id,
          'membersList': members
      },
      'operator': 'ADD'
  }

  response = user_list_service.mutateMembers([mutate_members_operation])

  if 'userLists' in response:
    for user_list in response['userLists']:
      print ('User list with name "%s" and ID "%d" was added.'
             % (user_list['name'], user_list['id']))


def NormalizeAndSHA256(s):
  """Normalizes (lowercase, remove whitespace) and hashes a string with SHA-256.

  Args:
    s: The string to perform this operation on.

  Returns:
    A normalized and SHA-256 hashed string.
  """
  return hashlib.sha256(s.strip().lower()).hexdigest()


if __name__ == '__main__':
  # Initialize client object.
  adwords_client = adwords.AdWordsClient.LoadFromStorage()
  main(adwords_client)

Create rule-based user lists

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

"""This example adds two rule-based remarketing user lists.

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.

The LoadFromStorage method is pulling credentials and properties from a
"googleads.yaml" file. By default, it looks for this file in your home
directory. For more information, see the "Caching authentication information"
section of our README.

"""


import calendar
from datetime import date
from datetime import datetime
from datetime import timedelta
from googleads import adwords


def main(client):
  # Initialize appropriate service.
  adwords_user_list_service = client.GetService(
      'AdwordsUserListService', version='v201809')

  # First rule item group - users who visited the checkout page and had more
  # than one item in their shopping cart.
  checkout_rule_item = {
      'StringRuleItem': {
          'key': {
              'name': 'ecomm_pagetype'
          },
          'op': 'EQUALS',
          'value': 'checkout'
      }
  }

  cart_size_rule_item = {
      'NumberRuleItem': {
          'key': {
              'name': 'cartsize'
          },
          'op': 'GREATER_THAN',
          'value': '1.0'
      }
  }

  # Combine the two rule items into a RuleItemGroup so AdWords will logically
  # AND the rules together.
  checkout_multiple_item_group = {
      'items': [checkout_rule_item, cart_size_rule_item]
  }

  # Second rule item group - users who checked out within the next 3 months.
  today = date.today()
  start_date_rule_item = {
      'DateRuleItem': {
          'key': {
              'name': 'checkoutdate'
          },
          'op': 'AFTER',
          'value': today.strftime('%Y%m%d')
      }
  }

  three_months_later = AddMonths(today, 3)

  three_months_later_rule_item = {
      'DateRuleItem': {
          'key': {
              'name': 'checkoutdate'
          },
          'op': 'BEFORE',
          'value': three_months_later.strftime('%Y%m%d')
      }
  }

  # Combine the date rule items into a RuleItemGroup
  checked_out_date_range_item_group = {
      'items': [start_date_rule_item, three_months_later_rule_item]
  }

  # Combine the rule item groups into a Rule so AdWords knows how to apply the
  # rules.
  rule = {
      'groups': [
          checkout_multiple_item_group,
          checked_out_date_range_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.
  site1_rule_item = {
      'StringRuleItem': {
          'key': {'name': 'url__'},
          'op': 'EQUALS',
          'value': 'example.com/example1'
      }
  }
  site2_rule_item = {
      'StringRuleItem': {
          'key': {'name': 'url__'},
          'op': 'EQUALS',
          'value': 'example.com/example2'
      }
  }

  # Create two rules to show that a visitor browsed two sites.
  user_visited_site1_rule = {
      'groups': [{
          'items': [site1_rule_item]
      }]
  }

  user_visited_site2_rule = {
      'groups': [{
          'items': [site2_rule_item]
      }]
  }

  # Create the user list with no restrictions on site visit date.
  expression_user_list = {
      'xsi_type': 'ExpressionRuleUserList',
      'name': 'Expression-based user list created at %s'
              % datetime.today().strftime('%Y%m%d %H:%M:%S'),
      'description': 'Users who checked out in three month window OR visited'
                     ' the checkout page with more than one item in their'
                     ' cart.',
      'rule': rule,
      # Optional: Set the populationStatus 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.
  end_date = AddMonths(today, 6)

  date_user_list = {
      'xsi_type': 'DateSpecificRuleUserList',
      'name': 'Date rule user list created at %s'
              % datetime.today().strftime('%Y%m%d %H:%M:%S'),
      'description': 'Users who visited the site between %s and %s and checked'
                     ' out in three month window OR visited the checkout page'
                     ' with more than one item in their cart.'
                     % (today.strftime('%Y%m%d'), end_date.strftime('%Y%m%d')),
      'rule': rule,
      'startDate': today.strftime('%Y%m%d'),
      'endDate': end_date.strftime('%Y%m%d')
  }

  # Create the user list for "Visitors of a page who did visit another page".
  # To create a user list for "Visitors of a page who did not visit another
  # page", change the ruleOperator from AND to AND_NOT.
  combined_user_list = {
      'xsi_type': 'CombinedRuleUserList',
      'name': 'Combined rule user lst create 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.
  operations = [
      {
          'operand': user_list,
          'operator': 'ADD',
      } for user_list in [expression_user_list, date_user_list,
                          combined_user_list]
  ]

  # Submit the operations.
  user_lists = adwords_user_list_service.mutate(operations)

  # Display results.
  for user_list in user_lists['value']:
    print (('User list added with ID %d, name "%s", status "%s", list type'
            ' "%s", accountUserListStatus "%s", description "%s".') %
           (user_list['id'], user_list['name'],
            user_list['status'], user_list['listType'],
            user_list['accountUserListStatus'], user_list['description']))


def AddMonths(start_date, months):
  """A simple convenience utility for adding months to a given start date.

  This increments the months by adding the number of days in the current month
  to the current month, for each month.

  Args:
    start_date: date The date months are being added to.
    months: int The number of months to add.

  Returns:
    A date equal to the start date incremented by the given number of months.
  """
  current_date = start_date
  i = 0
  while i < months:
    month_days = calendar.monthrange(current_date.year, current_date.month)[1]
    current_date += timedelta(days=month_days)
    i += 1
  return current_date


if __name__ == '__main__':
  # Initialize client object.
  adwords_client = adwords.AdWordsClient.LoadFromStorage()

  main(adwords_client)

Import conversion adjustments for existing conversions

#!/usr/bin/env python
#
# Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     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.

"""Imports conversion adjustments for conversions that already exist.

To set up a conversion tracker, run the add_conversion_tracker.py example.

"""

from googleads import adwords


CONVERSION_NAME = 'INSERT_CONVERSION_NAME_HERE'

GCLID = 'INSERT_GCLID_HERE'

ADJUSTMENT_TYPE = 'INSERT_ADJUSTMENT_TYPE_HERE'

CONVERSION_TIME = 'INSERT_CONVERSION_TIME_HERE'

ADJUSTMENT_TIME = 'INSERT_ADJUSTMENT_TIME_HERE'

ADJUSTED_VALUE = 'INSERT_ADJUST_VALUE_HERE'


def main(client, conversion_name, gclid, adjustment_type, conversion_time,
         adjustment_time, adjusted_value):
  # Initialize appropriate services.
  offline_conversion_service = client.GetService(
      'OfflineConversionAdjustmentFeedService', version='v201809')

  # Associate conversion adjustments with the existing named conversion tracker.
  # The GCLID should have been uploaded before with a conversion.
  feed = {
      'xsi_type': 'GclidOfflineConversionAdjustmentFeed',
      'conversionName': conversion_name,
      'googleClickId': gclid,
      'conversionTime': conversion_time,
      'adjustmentType': adjustment_type,
      'adjustmentTime': adjustment_time,
      'adjustedValue': adjusted_value
  }

  offline_conversion_operation = {'operator': 'ADD', 'operand': feed}

  offline_conversion_response = offline_conversion_service.mutate(
      [offline_conversion_operation])

  new_feed = offline_conversion_response['value'][0]

  print ('Uploaded offline conversion adjustment value of "%s" for Google '
         'Click ID "%s"' % (new_feed['adjustedValue'],
                            new_feed['googleClickId']))

if __name__ == '__main__':
  # Initialize client object.
  adwords_client = adwords.AdWordsClient.LoadFromStorage()

  main(adwords_client, CONVERSION_NAME, GCLID, ADJUSTMENT_TYPE, CONVERSION_TIME,
       ADJUSTMENT_TIME, ADJUSTED_VALUE)

Import offline call conversions

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

"""Imports offline conversion values for calls related to ads in your account.

To set up a conversion tracker, run the add_conversion_trackers.py example.

The LoadFromStorage method is pulling credentials and properties from a
"googleads.yaml" file. By default, it looks for this file in your home
directory. For more information, see the "Caching authentication information"
section of our README.

"""

from googleads import adwords


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
CALL_START_TIME = 'INSERT_CALL_START_TIME'
CONVERSION_NAME = 'INSERT_CONVERSION_NAME_HERE'  # Name of conversion tracker.
CONVERSION_TIME = 'INSERT_CONVERSION_TIME_HERE'
CONVERSION_VALUE = 'INSERT_CONVERSION_VALUE_HERE'


def main(client, caller_id, call_start_time, conversion_name, conversion_time,
         conversion_value):
  # Initialize appropriate services.
  occ_feed_service = client.GetService(
      'OfflineCallConversionFeedService', version='v201809')

  # 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.
  feed = {
      'callerId': caller_id,
      'callStartTime': call_start_time,
      'conversionName': conversion_name,
      'conversionTime': conversion_time,
      'conversionValue': conversion_value,
  }

  occ_operations = [{'operator': 'ADD', 'operand': feed}]

  occ_response = occ_feed_service.mutate(occ_operations)
  values = occ_response['value']

  if values:
    for occ_feed in values:
      print ('Uploaded offline call conversion value of "%s" for caller ID '
             '"%s".\n' % (occ_feed['conversionName'], occ_feed['callerId']))
  else:
    print 'No offline call conversions were added.'


if __name__ == '__main__':
  # Initialize client object.
  adwords_client = adwords.AdWordsClient.LoadFromStorage()

  main(adwords_client, CALLER_ID, CALL_START_TIME, CONVERSION_NAME,
       CONVERSION_TIME, CONVERSION_VALUE)

Import offline click conversions

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

"""Imports offline conversion values for specific clicks into your account.

To get the Google Click ID for a click, run a CLICK_PERFORMANCE_REPORT. To set
up a conversion tracker, run the add_conversion_trackers.py example.

The LoadFromStorage method is pulling credentials and properties from a
"googleads.yaml" file. By default, it looks for this file in your home
directory. For more information, see the "Caching authentication information"
section of our README.

"""

from googleads import adwords

# Name of the conversion tracker to upload to.
CONVERSION_NAME = 'INSERT_CONVERSION_NAME_HERE'
# Your click ID must be less than 30 days old.
CLICK_ID = 'INSERT_GOOGLE_CLICK_ID_HERE'
# The conversion time must be more recent than the time of the click.
CONVERSION_TIME = 'INSERT_CONVERSION_TIME_HERE'
CONVERSION_VALUE = 'INSERT_CONVERSION_VALUE_HERE'


def main(client, conversion_name, click_id, conversion_time, conversion_value):
  # Initialize appropriate services.
  offline_conversion_feed_service = client.GetService(
      'OfflineConversionFeedService', version='v201809')

  # 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.
  feed = {
      'conversionName': conversion_name,
      'conversionTime': conversion_time,
      'conversionValue': conversion_value,
      'googleClickId': click_id,
      # Optional: To upload fractional conversion credits, set the external
      # attribution model and credit. To use this feature, your conversion
      # tracker should be marked as externally attributed. To learn more about
      # importing externally attributed conversins, see:
      # https://developers.google.com/adwords/api/docs/guides/conversion-tracking#importing_externally_attributed_conversions
      # 'externalAttributionCredit': 0.3,
      # 'externalAttributionModel': 'Linear'
  }

  offline_conversion_operation = {
      'operator': 'ADD',
      'operand': feed
  }

  offline_conversion_response = offline_conversion_feed_service.mutate(
      [offline_conversion_operation])
  new_feed = offline_conversion_response['value']

  print ('Uploaded offline conversion value of "%s" for Google Click ID '
         '"%s" to "%s".' % (new_feed['conversionValue'],
                            new_feed['googleClickId'],
                            new_feed['conversionName']))


if __name__ == '__main__':
  # Initialize client object.
  adwords_client = adwords.AdWordsClient.LoadFromStorage()

  main(adwords_client, CONVERSION_NAME, CLICK_ID, CONVERSION_TIME,
       CONVERSION_VALUE)

Upload offline data for store sales transactions

#!/usr/bin/env python
#
# 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 shows how to upload offline data for store sales transactions.

The LoadFromStorage method is pulling credentials and properties from a
"googleads.yaml" file. By default, it looks for this file in your home
directory. For more information, see the "Caching authentication information"
section of our README.

"""

import datetime
import hashlib

from googleads import adwords
import pytz


_DT_FORMAT = '%Y%m%d %H%M%S'
# User identifier types whose values must be hashed.
_HASHED_IDENTIFIER_TYPES = ('HASHED_EMAIL', 'HASHED_FIRST_NAME',
                            'HASHED_LAST_NAME', 'HASHED_PHONE')
# The timezone to be used in this example. For valid timezone IDs, see:
# https://developers.google.com/adwords/api/docs/appendix/codes-formats#timezone-ids
_TIMEZONE = pytz.timezone('America/New_York')
# Name of the conversion tracker to upload to.
CONVERSION_NAME = 'INSERT_CONVERSION_NAME_HERE'
# Insert email addresses below for creating user identifiers.
EMAIL_ADDRESSES = ['EMAIL_ADDRESS_1', 'EMAIL_ADDRESS_2']
# The external upload ID can be any number that you use to keep track of your
# uploads.
EXTERNAL_UPLOAD_ID = 'INSERT_EXTERNAL_UPLOAD_ID'
# Store sales upload common metadata types
METADATA_TYPE_1P = 'FirstPartyUploadMetadata'
METADATA_TYPE_3P = 'ThirdPartyUploadMetadata'
# Set the below constant to METADATA_TYPE_3P if uploading third-party data.
STORE_SALES_UPLOAD_COMMON_METADATA_TYPE = METADATA_TYPE_1P
# The three constants below are needed when uploading third-party data. They
# are not used when uploading first-party data.
# Advertiser upload time to partner.
# For times, use the format yyyyMMdd HHmmss tz. For more details on formats,
# see:
# https://developers.google.com/adwords/api/docs/appendix/codes-formats#timezone-ids
ADVERTISER_UPLOAD_TIME = 'INSERT_ADVERTISER_UPLOAD_TIME_HERE'
# Indicates the version of the bridge map.
BRIDGE_MAP_VERSION_ID = 'INSERT_BRIDGE_MAP_VERSION_ID_HERE'
# The ID of the third party uploading the transaction feed.
PARTNER_ID = 'INSERT_PARTNER_ID_HERE'


def main(client, conversion_name, external_upload_id,
         store_sales_upload_common_metadata_type, email_addresses,
         advertiser_upload_time=None, bridge_map_version_id=None,
         partner_id=None):
  # Set partial failure to True since this example demonstrates how to handle
  # partial errors.
  client.partial_failure = True
  # Initialize appropriate services.
  offline_data_upload_service = client.GetService(
      'OfflineDataUploadService', version='v201809')

  # Create the first offline data for upload.
  # This transaction occurred 7 days ago with an amount of $200 USD.
  transaction_time_1 = (datetime.datetime.now(tz=_TIMEZONE) -
                        datetime.timedelta(days=7))
  offline_data_1 = {
      'StoreSalesTransaction': {
          'userIdentifiers': [
              _CreateUserIdentifier(identifier_type='HASHED_EMAIL',
                                    value=email_addresses[0]),
              _CreateUserIdentifier(identifier_type='STATE', value='New York')
          ],
          'transactionTime': _GetFormattedDateTime(transaction_time_1),
          'transactionAmount': {
              'currencyCode': 'USD',
              'money': {
                  'microAmount': 200000000
              }
          },
          'conversionName': conversion_name
      }
  }

  # Create the second offline data for upload.
  # This transaction occurred 14 days ago with amount of 450 EUR.
  transaction_time_2 = (datetime.datetime.now(tz=_TIMEZONE) -
                        datetime.timedelta(days=14))
  offline_data_2 = {
      'StoreSalesTransaction': {
          'userIdentifiers': [
              _CreateUserIdentifier(identifier_type='HASHED_EMAIL',
                                    value=email_addresses[1]),
              _CreateUserIdentifier(identifier_type='STATE', value='California')
          ],
          'transactionTime': _GetFormattedDateTime(transaction_time_2),
          'transactionAmount': {
              'currencyCode': 'EUR',
              'money': {
                  'microAmount': 450000000
              }
          },
          'conversionName': conversion_name
      }
  }

  # Set the type and metadata of this upload.
  upload_metadata = {
      'StoreSalesUploadCommonMetadata': {
          'xsi_type': store_sales_upload_common_metadata_type,
          'loyaltyRate': 1.0,
          'transactionUploadRate': 1.0,
      }
  }

  if store_sales_upload_common_metadata_type == METADATA_TYPE_1P:
    upload_type = 'STORE_SALES_UPLOAD_FIRST_PARTY'
  elif store_sales_upload_common_metadata_type == METADATA_TYPE_3P:
    upload_type = 'STORE_SALES_UPLOAD_THIRD_PARTY'
    upload_metadata['StoreSalesUploadCommonMetadata'].update({
        'advertiserUploadTime': advertiser_upload_time,
        'validTransactionRate': 1.0,
        'partnerMatchRate': 1.0,
        'partnerUploadRate': 1.0,
        'bridgeMapVersionId': bridge_map_version_id,
        'partnerId': partner_id
    })
  else:
    raise ValueError('Unknown metadata type.')

  # Create offline data upload
  offline_data_upload = {
      'externalUploadId': external_upload_id,
      'offlineDataList': [offline_data_1, offline_data_2],
      # Set the type of this upload.
      'uploadType': upload_type,
      'uploadMetadata': upload_metadata
  }

  # Create an offline data upload operation.
  operations = [{
      'operator': 'ADD',
      'operand': offline_data_upload
  }]

  # Upload offline data on the server and print the result.
  result = offline_data_upload_service.mutate(operations)
  offline_data_upload = result['value'][0]

  print ('Uploaded offline data with external upload ID "%d" and upload status '
         '"%s".' % (offline_data_upload['externalUploadId'],
                    offline_data_upload['uploadStatus']))

  # Print any partial data errors from the response.
  if result['partialFailureErrors']:
    for api_error in result['partialFailureErrors']:
      # Get the index of the failed operation from the error's field path
      # elements.
      operation_index = _GetFieldPathElementIndex(api_error, 'operations')

      if operation_index:
        failed_offline_data_upload = operations[operation_index]['operand']
        # Get the index of the entry in the offline data list from the error's
        # field path elements.
        offline_data_list_index = _GetFieldPathElementIndex(
            api_error, 'offlineDataList')
        print ('Offline data list entry "%d" in operation "%d" with external '
               'upload ID "%d" and type "%s" has triggered failure for the '
               'following reason: "%s"' % (
                   offline_data_list_index, operation_index,
                   failed_offline_data_upload['externalUploadId'],
                   failed_offline_data_upload['uploadType'],
                   api_error['errorString']))
      else:
        print ('A failure has occurred for the following reason: "%s".' % (
            api_error['errorString']))


def _CreateUserIdentifier(identifier_type=None, value=None):
  """Creates a user identifier from the specified type and value.

  Args:
    identifier_type: a str specifying the type of user identifier.
    value: a str value of the identifier; to be hashed using SHA-256 if needed.

  Returns:
    A dict specifying a user identifier, with a value hashed using SHA-256 if
      needed.
  """
  if identifier_type in _HASHED_IDENTIFIER_TYPES:
    # If the user identifier type is a hashed type, normalize and hash the
    # value.
    value = hashlib.sha256(value.strip().lower()).hexdigest()

  user_identifier = {
      'userIdentifierType': identifier_type,
      'value': value
  }

  return user_identifier


def _GetFieldPathElementIndex(api_error, field):
  """Retrieve the index of a given field in the api_error's fieldPathElements.

  Args:
    api_error: a dict containing a partialFailureError returned from the AdWords
      API.
    field: a str field for which this determines the index in the api_error's
      fieldPathElements.

  Returns:
    An int index of the field path element, or None if the specified field can't
    be found in the api_error.
  """
  field_path_elements = api_error['fieldPathElements']

  if field_path_elements:
    found_index = [field_path_element['index']
                   for field_path_element in field_path_elements
                   if field_path_element['field'] == field]
    if found_index:
      return found_index

  return None


def _GetFormattedDateTime(dt):
  """Formats the given datetime and timezone for use with AdWords.

  Args:
    dt: a datetime instance.

  Returns:
    A str representation of the datetime in the correct format for AdWords.
  """
  return '%s %s' % (datetime.datetime.strftime(dt, _DT_FORMAT), _TIMEZONE.zone)


if __name__ == '__main__':
  # Initialize client object.
  adwords_client = adwords.AdWordsClient.LoadFromStorage()

  main(adwords_client, CONVERSION_NAME, EXTERNAL_UPLOAD_ID,
       STORE_SALES_UPLOAD_COMMON_METADATA_TYPE, EMAIL_ADDRESSES,
       ADVERTISER_UPLOAD_TIME, BRIDGE_MAP_VERSION_ID, PARTNER_ID)

Send feedback about...

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