Get started with Customer MatchStay organized with collectionsSave and categorize content based on your preferences.
AI-generated Key Takeaways
Customer Match lets advertisers target ads using their existing customer data, requiring uploading information like emails or phone numbers.
Account eligibility based on policy compliance and payment history is required to use Customer Match features.
The recommended workflow for Customer Match involves creating a customer list, usingOfflineUserDataJobfor bulk uploads, and running the job to process data.
Key best practices for Customer Match include using one account to modify a list, maximizing operations per request, and avoiding mixing create and remove operations in the same job.
After a job succeeds, you can verify the match rate, target the list, and note that list size in the UI is rounded and only shows active users.
Customer Match lets you use your online and offline data to reach and re-engage
with your customers across Search, the Shopping tab, Gmail, YouTube, and
Display. Using information that your customers have shared with you, Customer
Match targets ads to those customers and other customers like them. You can
upload customer relationship management (CRM) data in bulk, append or remove
data, or use these user lists to create alogical_user_list.
See the audience managementoverviewfor a list of different audience segment types to compare Customer Match with
other user list options.
Not every account is eligible to use Customer Match. To use Customer Match, your
account must have the following:
A good history of policy compliance
A good payment history
Depending on which requirements your account satisfies, different features are
available. Refer to the Customer Matchpolicyfor eligibility
requirements and restrictions.
Before you start: Plan your implementation
Before you start writing any code, it's important to plan your Customer Match
implementation. This section outlines the overall process and key considerations
to help you design a robust integration. Once you have a clear plan, you can
proceed with the implementation steps that follow.
Usage flow
Here is the recommended flow for creating and targeting a customer list:
Create an empty customer list.
Create anOfflineUserDataJob. It's
far more efficient to create a single large job than several
smaller jobs.
If a user has denied consent, you could create a job with aremoveoperation to remove the user's identifiers from the user list.
If you are missing consent for specific users, create a separate job where
you don't set theconsentfield of the job'scustomer_match_user_list_metadata, then add identifiers for those users
usingcreateoperations for that separate job.
Add operations using theOfflineUserDataJobService.AddOfflineUserDataJobOperationsmethod. We recommend adding up to 10,000 total identifiers in a single call
for optimal processing. A singleAddOfflineUserDataJobOperationsrequest
can contain at most 100,000 identifiers across all of theUserDataobjects in the operations list.
For example, if each of yourUserDataobjects has oneUserIdentifierforhashed_emailand anotherUserIdentifierforhashed_phone_number, then
sending 5,000UserDataobjects per request is optimal since each request
will contain 10,000 user identifiers in total.
Repeat the previous step until all operations are added or until the job is
at capacity. There are no limits on the number of operations you can add to a
single job; however, we recommend no more than 1,000,000 operations per job
for optimal processing.
Run the job.You must run the job within 5 days of creating it. Otherwise,
the job will no longer be available for running.
Most developers use this service. It is optimized for large uploads with
high throughput and returns success metrics upon completion. This guide
focuses primarily on this service.
This service is optimized to upload a small number of identifiers at a time
with sporadic updates and is not optimized to run continuously. It has a limit
of 10 operations per request. In addition, a single request can't contain more
than 100 items in total across alluser_identifiers.
For guidance on uploads
with this service, visit theguidefor managing your Customer Match integration.
Starting with version v15 of
the Google Ads API, you should populate theconsentfield ofcustomer_match_user_list_metadatain yourUploadUserDataRequestcreaterequests. Forremoverequests, consent is not needed.
The API returns a response withreceived_operations_countset to zero indicating the request was not processed ifconsent.ad_user_dataorconsent.ad_personalizationisDENIED. If an identifier has a consent ofDENIED,
then you could request to remove the identifier from the user list using theremoveoperation ofUploadUserDataRequest.
Best practices
Keep the following best practices in mind when designing your Customer Match
integration:
Don't attempt to use multiple accounts to modify a single user list. A user
list can only be modified by the Google Ads ordata
partneraccount that
created it.
Maximize the number of operations perAddOfflineUserDataJobOperationsRequest, up to 100,000 identifiers, to
avoidRESOURCE_EXHAUSTEDerrors.
Avoid simultaneously running multipleOfflineUserDataJobprocesses that
modify the same user list (that is, multiple jobs whoseCustomerMatchUserListMetadata.user_listpoint to the same resource name). Doing so can result in aCONCURRENT_MODIFICATIONerror since multiple jobs are not permitted to operate on the same list at
the same time. This error can also occur if attempting to simultaneously
modify a list through the Google Ads UI and the Google Ads API. Note that this does not
apply toadding
operationsto aPENDINGjob, which can be done at any time before the job is started.
If you have thousands of operations to apply, createoneOfflineUserDataJobcontainingallof the operations. Don't create several
jobs with only a few hundred operations each and run them sequentially or
simultaneously. A single large job with all of your operations is far
more efficient than multiple small jobs, and reduces the chances of you
encountering errors in your workflow.
For ideas on how to leverage your customer lists for optimal targeting, visit
theHelp Center.
Step 1: Create a customer list
Create a customer list with theUserListService. Customer lists are created
by setting thecrm_based_user_listfield on theuser_listobject. Thecrm_based_user_listfield can be set on campaign types
that support targeting customer lists:
Customer Match in different campaign types
Search network
Ads show on the search network.
Display network
Ads show on the display network, and on Gmail only if there are GSP
creatives.
Ads show on the search network, and on Gmail only if there are GSP
creatives.
Video campaigns
Ads show on YouTube only if there are in-stream TrueView ads.
Shopping campaigns
Ads show in the Shopping tab.
crm_based_user_listcontains three fields:
app_id: A string that uniquely identifies the mobile app from which the
data was collected. This is required when creatingCrmBasedUserListfor
uploading mobile advertising IDs.
upload_key_type:
A matching key type of the list, which can beCONTACT_INFO,CRM_ID, orMOBILE_ADVERTISING_ID. Mixed data types are not allowed in the same list.
This field is required for all customer lists.
data_source_type:
The data source of the list. The default value isFIRST_PARTY. Allowlisted
customers may create third-party sourced customer lists.
Themembership_life_spanattribute of the user list lets you define the time period, in days, for which a
user is considered to be in the list. Themembership_life_spanof a Customer
Match user list must be no more than540, which is also the default value.
Themembership_statusattribute
defines whether the list accepts new users.
defcreate_customer_match_user_list(client:GoogleAdsClient,customer_id:str)->str:"""Creates a Customer Match user list.Args:client: The Google Ads client.customer_id: The ID for the customer that owns the user list.Returns:The string resource name of the newly created user list."""# Creates the UserListService client.user_list_service_client:UserListServiceClient=client.get_service("UserListService")# Creates the user list operation.user_list_operation:UserListOperation=client.get_type("UserListOperation")# Creates the new user list.user_list:UserList=user_list_operation.createuser_list.name=f"Customer Match list #{uuid.uuid4()}"user_list.description=("A list of customers that originated from email and physical addresses")# Sets the upload key type to indicate the type of identifier that is used# to add users to the list. This field is immutable and required for a# CREATE operation.user_list.crm_based_user_list.upload_key_type=(client.enums.CustomerMatchUploadKeyTypeEnum.CONTACT_INFO)# Membership life span must be between 0 and 540 days inclusive. See:# https://developers.google.com/google-ads/api/reference/rpc/latest/UserList#membership_life_span# Sets the membership life span to 30 days.user_list.membership_life_span=30response:MutateUserListsResponse=(user_list_service_client.mutate_user_lists(customer_id=customer_id,operations=[user_list_operation]))user_list_resource_name:str=response.results[0].resource_nameprint(f"User list with resource name '{user_list_resource_name}' was created.")returnuser_list_resource_name
defcreate_customer_match_user_list(client,customer_id)# Creates the user list.operation=client.operation.create_resource.user_listdo|ul|ul.name="Customer Match List#{(Time.new.to_f*1000).to_i}"ul.description="A list of customers that originated from email and "\"physical addresses"# Membership life span must be between 0 and 540 days inclusive. See:# https://developers.google.com/google-ads/api/reference/rpc/latest/UserList#membership_life_span# Sets the membership life span to 30 days.ul.membership_life_span=30ul.crm_based_user_list=client.resource.crm_based_user_list_infodo|crm|crm.upload_key_type=:CONTACT_INFOendend# Issues a mutate request to add the user list and prints some information.response=client.service.user_list.mutate_user_lists(customer_id:customer_id,operations:[operation],)# Prints out some information about the newly created user list.resource_name=response.results.first.resource_nameputs"User list with resource name#{resource_name}was created."resource_nameend
subcreate_customer_match_user_list{my($api_client,$customer_id)=@_;# Create the user list.my$user_list=Google::Ads::GoogleAds::V22::Resources::UserList->new({name=>"Customer Match list #".uniqid(),description=>"A list of customers that originated from email and physical addresses",# Membership life span must be between 0 and 540 days inclusive. See:# https://developers.google.com/google-ads/api/reference/rpc/latest/UserList#membership_life_span# Set the membership life span to 30 days.membershipLifeSpan=>30,# Set the upload key type to indicate the type of identifier that will be# used to add users to the list. This field is immutable and required for# a CREATE operation.crmBasedUserList=>Google::Ads::GoogleAds::V22::Common::CrmBasedUserListInfo->new({uploadKeyType=>CONTACT_INFO})});# Create the user list operation.my$user_list_operation=Google::Ads::GoogleAds::V22::Services::UserListService::UserListOperation->new({create=>$user_list});# Issue a mutate request to add the user list and print some information.my$user_lists_response=$api_client->UserListService()->mutate({customerId=>$customer_id,operations=>[$user_list_operation]});my$user_list_resource_name=$user_lists_response->{results}[0]{resourceName};printf"User list with resource name '%s' was created.\n",$user_list_resource_name;return$user_list_resource_name;}
The three primary match keys are email address, mailing address, and phone
number. You can use user ID and mobile device ID as match keys, but these
solutions are less future-proof given their reliance on cookies and device
ID. We recommend uploading user contact information—such as email address,
mailing address, and phone number—when possible instead of CRM or mobile
IDs.
Each user list can only contain a single type of customer data as specified by
theCrmBasedUserListInfo.upload_key_typefield. Furthermore, aUserDataobject, which
represents a single user, can contain up to 20 user identifiers, each of which
has its ownUserIdentifierobject. More than 20 identifiers results in aTOO_MANY_USER_IDENTIFIERSerror.
Google Ads only uses a Customer Match user list for targeting if it has met a
minimum threshold of active users at the time the ad is served; active users is
the number of users on your list who are active on Gmail, Search, YouTube, or
Display. Upload at least 5,000 members to increase the chance of having enough
matched, active users for targeting.
Upload user contact information
To upload user email addresses, mailing addresses, or phone numbers, setupload_key_typetoCONTACT_INFO. Note that contact information must be
associated with a Google Account in order to be matched, and corporate
accounts—such as Google Workspace—cannot be targeted.
For privacy concerns, email addresses, first names, last names, and phone
numbers must be hashed using the SHA-256 algorithm before being uploaded. In
order to standardize the hash results, prior to hashing one of these values,
make sure you perform the following tasks:
Remove leading and trailing whitespaces.
For names, email addresses, and mailing addresses: Convert text to lowercase.
For phone numbers: Convert each phone number toE164
formatbefore hashing. This format represents
a phone number as a number up to fifteen digits in length starting with a+sign—for example,+12125650000or+442070313000. The leading+sign can optionally be omitted.
For email addresses, you don't have to remove all periods (.) preceding the
domain name ingmail.comandgooglemail.comemail addresses since they're
still accepted.
If the contact information is not correctly formatted before hashing, the API
still accepts the hashed information, but it can't be matched with a customer.
If you want to upload mailing address data, you must include at least:
Country code
Postal code
Hashed first name
Hashed last name
If any of these fields is missing, the address can't be matched.
While customer lists can contain only oneupload_key_type, multiple types of
contact information can be uploaded for anupload_key_typeofCONTACT_INFO.
This is recommended for increased match rates.
Upload CRM IDs
To populate the customer list with CRM IDs, setupload_key_typetoCRM_ID.
CRM IDs are matched from a user ID generated and assigned by the advertiser.
This is similar to uploadingMOBILE_ADVERTISING_IDinstances, but you instead
populate thethird_party_user_idfield of theUserIdentifierobject.
Upload mobile IDs
Similarly to Customer Match with emails, you can perform customer matching using
Identifier for Advertising (IDFA) or Google Advertising ID (AAID) mobile device
IDs. To do so, specify theapp_idproperty and setupload_key_typetoMOBILE_ADVERTISING_IDprior to using a user list for customer matching with mobile device IDs.
Code example
The following example uses anOfflineUserDataJobOperationto append customer
contact information to a customer list.
defbuild_offline_user_data_job_operations(client:GoogleAdsClient,)->List[OfflineUserDataJobOperation]:"""Creates a raw input list of unhashed user information.Each element of the list represents a single user and is a dict containing aseparate entry for the keys "email", "phone", "first_name", "last_name","country_code", and "postal_code". In your application, this data might comefrom a file or a database.Args:client: The Google Ads client.Returns:A list containing the operations."""# The first user data has an email address and a phone number.raw_record_1:Dict[str,str]={"email":"dana@example.com",# Phone number to be converted to E.164 format, with a leading '+' as# required. This includes whitespace that will be removed later."phone":"+1 800 5550101",}# The second user data has an email address, a mailing address, and a phone# number.raw_record_2:Dict[str,str]={# Email address that includes a period (.) before the email domain."email":"alex.2@example.com",# Address that includes all four required elements: first name, last# name, country code, and postal code."first_name":"Alex","last_name":"Quinn","country_code":"US","postal_code":"94045",# Phone number to be converted to E.164 format, with a leading '+' as# required."phone":"+1 800 5550102",}# The third user data only has an email address.raw_record_3:Dict[str,str]={"email":"charlie@example.com"}# Adds the raw records to a raw input list.raw_records:List[Dict[str,str]]=[raw_record_1,raw_record_2,raw_record_3,]operations:List[OfflineUserDataJobOperation]=[]# Iterates over the raw input list and creates a UserData object for each# record.forrecordinraw_records:# Creates a UserData object that represents a member of the user list.user_data:UserData=client.get_type("UserData")# Checks if the record has email, phone, or address information, and# adds a SEPARATE UserIdentifier object for each one found. For example,# a record with an email address and a phone number will result in a# UserData with two UserIdentifiers.# IMPORTANT: Since the identifier attribute of UserIdentifier# (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)# is a oneof# (https://protobuf.dev/programming-guides/proto3/#oneof-features), you# must set only ONE of hashed_email, hashed_phone_number, mobile_id,# third_party_user_id, or address-info. Setting more than one of these# attributes on the same UserIdentifier will clear all the other members# of the oneof. For example, the following code is INCORRECT and will# result in a UserIdentifier with ONLY a hashed_phone_number:# incorrect_user_identifier = client.get_type("UserIdentifier")# incorrect_user_identifier.hashed_email = "..."# incorrect_user_identifier.hashed_phone_number = "..."# The separate 'if' statements below demonstrate the correct approach# for creating a UserData object for a member with multiple# UserIdentifiers.# Checks if the record has an email address, and if so, adds a# UserIdentifier for it.if"email"inrecord:user_identifier:UserIdentifier=client.get_type("UserIdentifier")user_identifier.hashed_email=normalize_and_hash(record["email"],True)# Adds the hashed email identifier to the UserData object's list.user_data.user_identifiers.append(user_identifier)# Checks if the record has a phone number, and if so, adds a# UserIdentifier for it.if"phone"inrecord:user_identifier:UserIdentifier=client.get_type("UserIdentifier")user_identifier.hashed_phone_number=normalize_and_hash(record["phone"],True)# Adds the hashed phone number identifier to the UserData object's# list.user_data.user_identifiers.append(user_identifier)# Checks if the record has all the required mailing address elements,# and if so, adds a UserIdentifier for the mailing address.if"first_name"inrecord:required_keys=("last_name","country_code","postal_code")# Checks if the record contains all the other required elements of# a mailing address.ifnotall(keyinrecordforkeyinrequired_keys):# Determines which required elements are missing from the# record.missing_keys=record.keys()-required_keysprint("Skipping addition of mailing address information ""because the following required keys are missing: "f"{missing_keys}")else:user_identifier:UserIdentifier=client.get_type("UserIdentifier")address_info:AddressInfo=user_identifier.address_infoaddress_info.hashed_first_name=normalize_and_hash(record["first_name"],False)address_info.hashed_last_name=normalize_and_hash(record["last_name"],False)address_info.country_code=record["country_code"]address_info.postal_code=record["postal_code"]user_data.user_identifiers.append(user_identifier)# If the user_identifiers repeated field is not empty, create a new# OfflineUserDataJobOperation and add the UserData to it.ifuser_data.user_identifiers:operation:OfflineUserDataJobOperation=client.get_type("OfflineUserDataJobOperation")operation.create=user_dataoperations.append(operation)
# Create a list of unhashed user data records that we will format in the# following steps to prepare for the API.raw_records=[# The first user data has an email address and a phone number.{email:'dana@example.com',# Phone number to be converted to E.164 format, with a leading '+' as# required. This includes whitespace that will be removed later.phone:'+1 800 5550100',},# The second user data has an email address, a phone number, and an address.{# Email address that includes a period (.) before the Gmail domain.email:'alex.2@example.com',# Address that includes all four required elements: first name, last# name, country code, and postal code.first_name:'Alex',last_name:'Quinn',country_code:'US',postal_code:'94045',# Phone number to be converted to E.164 format, with a leading '+' as# required.phone:'+1 800 5550102',},# The third user data only has an email address.{email:'charlie@example.com',},]# Create a UserData for each entry in the raw records.user_data_list=raw_records.mapdo|record|client.resource.user_datado|data|ifrecord[:email]data.user_identifiers<<client.resource.user_identifierdo|ui|ui.hashed_email=normalize_and_hash(record[:email],true)endendifrecord[:phone]data.user_identifiers<<client.resource.user_identifierdo|ui|ui.hashed_phone_number=normalize_and_hash(record[:phone],true)endendifrecord[:first_name]# Check that we have all the required information.missing_keys=[:last_name,:country_code,:postal_code].reject{|key|record[key].nil?}ifmissing_keys.empty?# If nothing is missing, add the address.data.user_identifiers<<client.resource.user_identifierdo|ui|ui.address_identifier=client.resource.offline_user_address_infodo|address|address.hashed_first_name=normalize_and_hash(record[:first_name])address.hashed_last_name=normalize_and_hash(record[:last_name])address.country_code=record[:country_code]address.postal_code=record[:postal_code]endendelse# If some data is missing, skip this entry.puts"Skipping addition of mailing information because the following keys are missing:"\"#{missing_keys}"endendendendoperations=user_data_list.mapdo|user_data|client.operation.create_resource.offline_user_data_job(user_data)end
# The first user data has an email address and a phone number.my$raw_record_1={email=>'dana@example.com',# Phone number to be converted to E.164 format, with a leading '+' as# required. This includes whitespace that will be removed later.phone=>'+1 800 5550101',};# The second user data has an email address, a mailing address, and a phone# number.my$raw_record_2={# Email address that includes a period (.) before the Gmail domain.email=>'alex.2@example.com',# Address that includes all four required elements: first name, last# name, country code, and postal code.firstName=>'Alex',lastName=>'Quinn',countryCode=>'US',postalCode=>'94045',# Phone number to be converted to E.164 format, with a leading '+' as# required.phone=>'+1 800 5550102',};# The third user data only has an email address.my$raw_record_3={email=>'charlie@example.com',};my$raw_records=[$raw_record_1,$raw_record_2,$raw_record_3];my$operations=[];foreachmy$record(@$raw_records){# Check if the record has email, phone, or address information, and adds a# SEPARATE UserIdentifier object for each one found. For example, a record# with an email address and a phone number will result in a UserData with two# UserIdentifiers.## IMPORTANT: Since the identifier attribute of UserIdentifier# (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)# is a oneof# (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set# only ONE of hashed_email, hashed_phone_number, mobile_id, third_party_user_id,# or address-info. Setting more than one of these attributes on the same UserIdentifier# will clear all the other members of the oneof. For example, the following code is# INCORRECT and will result in a UserIdentifier with ONLY a hashed_phone_number:## my $incorrect_user_identifier = Google::Ads::GoogleAds::V22::Common::UserIdentifier->new({# hashedEmail => '...',# hashedPhoneNumber => '...',# });## The separate 'if' statements below demonstrate the correct approach for creating a# UserData object for a member with multiple UserIdentifiers.my$user_identifiers=[];# Check if the record has an email address, and if so, add a UserIdentifier for it.if(defined$record->{email}){# Add the hashed email identifier to the list of UserIdentifiers.push(@$user_identifiers,Google::Ads::GoogleAds::V22::Common::UserIdentifier->new({hashedEmail=>normalize_and_hash($record->{email},1)}));}# Check if the record has a phone number, and if so, add a UserIdentifier for it.if(defined$record->{phone}){# Add the hashed phone number identifier to the list of UserIdentifiers.push(@$user_identifiers,Google::Ads::GoogleAds::V22::Common::UserIdentifier->new({hashedPhoneNumber=>normalize_and_hash($record->{phone},1)}));}# Check if the record has all the required mailing address elements, and if so, add# a UserIdentifier for the mailing address.if(defined$record->{firstName}){my$required_keys=["lastName","countryCode","postalCode"];my$missing_keys=[];foreachmy$key(@$required_keys){if(!defined$record->{$key}){push(@$missing_keys,$key);}}if(@$missing_keys){print"Skipping addition of mailing address information because the following"."keys are missing: ".join(",",@$missing_keys);}else{push(@$user_identifiers,Google::Ads::GoogleAds::V22::Common::UserIdentifier->new({addressInfo=>Google::Ads::GoogleAds::V22::Common::OfflineUserAddressInfo->new({# First and last name must be normalized and hashed.hashedFirstName=>normalize_and_hash($record->{firstName}),hashedLastName=>normalize_and_hash($record->{lastName}),# Country code and zip code are sent in plain text.countryCode=>$record->{countryCode},postalCode=>$record->{postalCode},})}));}}# If the user_identifiers array is not empty, create a new# OfflineUserDataJobOperation and add the UserData to it.if(@$user_identifiers){my$user_data=Google::Ads::GoogleAds::V22::Common::UserData->new({userIdentifiers=>[$user_identifiers]});push(@$operations,Google::Ads::GoogleAds::V22::Services::OfflineUserDataJobService::OfflineUserDataJobOperation->new({create=>$user_data}));}}
Once theOfflineUserDataJobhas aSUCCESSstatus, the estimated match rate is
available in theoperation_metadata.match_rate_rangefield. If you query this field before the job completes, the value in this field
might be zero. To ensure your match rate is ready for verification and the list
ready for targeting, we recommend polling the job for completion. It can take as
little as 10 minutes or up to 24 hours for the job to complete.
defcheck_job_status(client:GoogleAdsClient,customer_id:str,offline_user_data_job_resource_name:str,)->None:"""Retrieves, checks, and prints the status of the offline user data job.If the job is completed successfully, information about the user list isprinted. Otherwise, a GAQL query will be printed, which can be used tocheck the job status at a later date.Offline user data jobs may take 6 hours or more to complete, so checking thestatus periodically, instead of waiting, can be more efficient.Args:client: The Google Ads client.customer_id: The ID for the customer that owns the user list.offline_user_data_job_resource_name: The resource name of the offlineuser data job to get the status of."""query:str=f"""SELECToffline_user_data_job.resource_name,offline_user_data_job.id,offline_user_data_job.status,offline_user_data_job.type,offline_user_data_job.failure_reason,offline_user_data_job.customer_match_user_list_metadata.user_listFROM offline_user_data_jobWHERE offline_user_data_job.resource_name ='{offline_user_data_job_resource_name}'LIMIT 1"""# Issues a search request using streaming.google_ads_service:GoogleAdsServiceClient=client.get_service("GoogleAdsService")results:SearchGoogleAdsStreamResponse=google_ads_service.search(customer_id=customer_id,query=query)offline_user_data_job_result:OfflineUserDataJob=next(iter(results)).offline_user_data_jobstatus_name:str=offline_user_data_job_result.status.nameuser_list_resource_name:str=(offline_user_data_job_result.customer_match_user_list_metadata.user_list)print(f"Offline user data job ID '{offline_user_data_job_result.id}' with type "f"'{offline_user_data_job_result.type_.name}' has status:{status_name}")ifstatus_name=="SUCCESS":print_customer_match_user_list_info(client,customer_id,user_list_resource_name)elifstatus_name=="FAILED":print(f"\tFailure Reason:{offline_user_data_job_result.failure_reason}")elifstatus_namein("PENDING","RUNNING"):print("To check the status of the job periodically, use the following "f"GAQL query with GoogleAdsService.Search:{query}")
defcheck_job_status(client,customer_id,offline_user_data_job)query=<<~QUERYSELECToffline_user_data_job.id,offline_user_data_job.status,offline_user_data_job.type,offline_user_data_job.failure_reason,offline_user_data_job.customer_match_user_list_metadata.user_listFROMoffline_user_data_jobWHEREoffline_user_data_job.resource_name='#{offline_user_data_job}'QUERYrow=client.service.google_ads.search(customer_id:customer_id,query:query,).firstjob=row.offline_user_data_jobputs"Offline user data job ID#{job.id}with type '#{job.type}' has status:#{job.status}."casejob.statuswhen:SUCCESSprint_customer_match_user_list(client,customer_id,job.customer_match_user_list_metadata.user_list)when:FAILEDputs" Failure reason:#{job.failure_reason}"elseputs" To check the status of the job periodically, use the following GAQL "\"query with GoogleAdsService.search:"putsqueryendend
subcheck_job_status{my($api_client,$customer_id,$offline_user_data_job_resource_name)=@_;my$search_query="SELECT offline_user_data_job.resource_name, "."offline_user_data_job.id, offline_user_data_job.status, "."offline_user_data_job.type, offline_user_data_job.failure_reason, "."offline_user_data_job.customer_match_user_list_metadata.user_list "."FROM offline_user_data_job "."WHERE offline_user_data_job.resource_name = "."'$offline_user_data_job_resource_name' LIMIT 1";my$search_request=Google::Ads::GoogleAds::V22::Services::GoogleAdsService::SearchGoogleAdsRequest->new({customerId=>$customer_id,query=>$search_query});# Get the GoogleAdsService.my$google_ads_service=$api_client->GoogleAdsService();my$iterator=Google::Ads::GoogleAds::Utils::SearchGoogleAdsIterator->new({service=>$google_ads_service,request=>$search_request});# The results have exactly one row.my$google_ads_row=$iterator->next;my$offline_user_data_job=$google_ads_row->{offlineUserDataJob};my$status=$offline_user_data_job->{status};printf"Offline user data job ID %d with type %s has status: %s.\n",$offline_user_data_job->{id},$offline_user_data_job->{type},$status;if($statuseqSUCCESS){print_customer_match_user_list_info($api_client,$customer_id,$offline_user_data_job->{customerMatchUserListMetadata}{userList});}elsif($statuseqFAILED){print"Failure reason: $offline_user_data_job->{failureReason}";}elsif(grep/$status/,(PENDING,RUNNING)){print"To check the status of the job periodically, use the following GAQL "."query with the GoogleAdsService->search() method:\n$search_query\n";}return1;}
googleads_service_client:GoogleAdsServiceClient=client.get_service("GoogleAdsService")# Creates a query that retrieves the user list.query:str=f"""SELECTuser_list.size_for_display,user_list.size_for_searchFROM user_listWHERE user_list.resource_name = '{user_list_resource_name}'"""# Issues a search request.search_results:SearchGoogleAdsStreamResponse=(googleads_service_client.search(customer_id=customer_id,query=query))
# Create a query that retrieves the user list.my$search_query="SELECT user_list.size_for_display, user_list.size_for_search "."FROM user_list "."WHERE user_list.resource_name = '$user_list_resource_name'";# Create a search Google Ads stream request that will retrieve the user list.my$search_stream_request=Google::Ads::GoogleAds::V22::Services::GoogleAdsService::SearchGoogleAdsStreamRequest->new({customerId=>$customer_id,query=>$search_query,});# Get the GoogleAdsService.my$google_ads_service=$api_client->GoogleAdsService();my$search_stream_handler=Google::Ads::GoogleAds::Utils::SearchStreamHandler->new({service=>$google_ads_service,request=>$search_stream_request});
For privacy purposes, the user list size shows as zero until the list has at
least 100 members. After that, the size is rounded to the two most significant
digits.
Errors during the execution of anOfflineUserDataJobcan be fetched through
theoffline_user_data_jobresource using the Google Ads Query Language. Note, however, that this report does not contain
information about any failed matches since only hashes are compared when
performing matches. Consult thetroubleshooting
guideif you
encounter issues with your customer lists.
Compare to the Google Ads UI
A list could appear smaller than expected when viewed in the Audience Manager
from the Google Ads UI. This view shows the number ofactive usersin the list.
For more information, see thistroubleshooting
guide.
Since it can take up to 24 hours for a list to be populated with members,
you might see anIn Progressstatusin the Google Ads UI if you
upload to an audience list more frequently than once every 12 hours.
Step 4: Target your list
You can target your list at the ad group level or at the campaign level. The
process is similar to other types of targeting criteria in the API.
Code example to target ads in ad group to a user list
deftarget_ads_in_ad_group_to_user_list(client:GoogleAdsClient,customer_id:str,ad_group_id:str,user_list_resource_name:str,)->str:"""Creates an ad group criterion that targets a user list with an ad group.Args:client: an initialized GoogleAdsClient instance.customer_id: a str client customer ID used to create an ad groupcriterion.ad_group_id: a str ID for an ad group used to create an ad groupcriterion that targets members of a user list.user_list_resource_name: a str resource name for a user list.Returns:a str resource name for an ad group criterion."""ad_group_criterion_operation:AdGroupCriterionOperation=client.get_type("AdGroupCriterionOperation")# Creates the ad group criterion targeting members of the user list.ad_group_criterion:AdGroupCriterion=ad_group_criterion_operation.createad_group_criterion.ad_group=client.get_service("AdGroupService").ad_group_path(customer_id,ad_group_id)ad_group_criterion.user_list.user_list=user_list_resource_namead_group_criterion_service:AdGroupCriterionServiceClient=(client.get_service("AdGroupCriterionService"))response:MutateAdGroupCriteriaResponse=(ad_group_criterion_service.mutate_ad_group_criteria(customer_id=customer_id,operations=[ad_group_criterion_operation]))resource_name:str=response.results[0].resource_nameprint("Successfully created ad group criterion with resource name: "f"'{resource_name}' targeting user list with resource name: "f"'{user_list_resource_name}' and with ad group with ID "f"{ad_group_id}.")returnresource_name
deftarget_ads_in_ad_group_to_user_list(client,customer_id,ad_group_id,user_list)# Creates the ad group criterion targeting members of the user list.operation=client.operation.create_resource.ad_group_criteriondo|agc|agc.ad_group=client.path.ad_group(customer_id,ad_group_id)agc.user_list=client.resource.user_list_infodo|info|info.user_list=user_listendend# Issues a mutate request to create the ad group criterion.response=client.service.ad_group_criterion.mutate_ad_group_criteria(customer_id:customer_id,operations:[operation],)ad_group_criterion_resource_name=response.results.first.resource_nameputs"Successfully created ad group criterion with resource name "\"'#{ad_group_criterion_resource_name}' targeting user list with resource name "\"'#{user_list}' with ad group with ID#{ad_group_id}"ad_group_criterion_resource_nameend
subtarget_ads_in_ad_group_to_user_list{my($api_client,$customer_id,$ad_group_id,$user_list_resource_name)=@_;# Create the ad group criterion targeting members of the user list.my$ad_group_criterion=Google::Ads::GoogleAds::V22::Resources::AdGroupCriterion->new({adGroup=>Google::Ads::GoogleAds::V22::Utils::ResourceNames::ad_group($customer_id,$ad_group_id),userList=>Google::Ads::GoogleAds::V22::Common::UserListInfo->new({userList=>$user_list_resource_name})});# Create the operation.my$ad_group_criterion_operation=Google::Ads::GoogleAds::V22::Services::AdGroupCriterionService::AdGroupCriterionOperation->new({create=>$ad_group_criterion});# Add the ad group criterion, then print and return the new criterion's resource name.my$ad_group_criteria_response=$api_client->AdGroupCriterionService()->mutate({customerId=>$customer_id,operations=>[$ad_group_criterion_operation]});my$ad_group_criterion_resource_name=$ad_group_criteria_response->{results}[0]{resourceName};printf"Successfully created ad group criterion with resource name '%s' "."targeting user list with resource name '%s' with ad group with ID %d.\n",$ad_group_criterion_resource_name,$user_list_resource_name,$ad_group_id;return$ad_group_criterion_resource_name;}
Acrm_based_user_listcan only be combined with anothercrm_based_user_listwhen using alogical_user_list.
All policies forcrm_based_user_listapply to the resulting user list.
Manage and update your lists
After you have created your customer lists, you'll likely need to update
them periodically to reflect changes in your customer base. The Google Ads API lets
you manage your existing lists by adding or removing members.
Key operations for updating your lists include:
Append new users: Add new customer data to an existing list.
Remove specific users: Remove individual users from a list based on their
identifiers.
Replace all members: Clear an existing list and replace its contents
entirely, although appending and removing is recommended instead.
[[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2025-11-05 UTC."],[],[]]