This guide explains how to migrate from building user lists with
AdwordsUserListService
in the AdWords API to using UserDataService
in
the Google Ads API in order to sync Customer Match data.
The Remarketing and Audience Targeting guide also provides useful context about user lists and audience management in the Google Ads API. However, it is not required for understanding the concepts in this guide.
Interface mapping
The following table provides a mapping of the equivalent components and corresponding reference documentation for the AdWords API and Google Ads API.
Note that there are two services available in Google Ads API:
UserDataService
and
OfflineUserDataJobService
. Both can
upload data for user lists, but
OfflineUserDataJobs
are processed
asynchronously. This document steps through the usage of UserDataService
.
Complete example code utilizing the OfflineUserDataService
is included at the
end of the page.
AdWords API Service | Equivalent Google Ads API Service | |
---|---|---|
UserList management | ||
Service |
AdWordsUserListService
|
UserListService
|
Customer Match UserList |
CrmBasedUserList
|
UserList.crm_based_user_list
|
Service-specific errors |
UserListError
|
UserListErrorEnum
|
UserList member uploads | ||
Service |
AdWordsUserListService
|
UserDataService
|
Request |
MutateMembersOperation
|
UploadUserDataRequest
|
Response |
MutateMembersReturnValue
|
UploadUserDataResponse
|
Service-specific errors |
MutateMembersError
|
UserDataError
|
Rate limits
The UserDataService
has a limit of 10
operations and 100 user IDs per request.
Creating and populating a Customer Match UserList
We now demonstrate how to create and populate a Customer Match user list using
UserDataService
.
Creating a UserList instance
First, create a Customer Match user list with the
UserListService
. Set the
crm_based_user_list
field with a
CrmBasedUserListInfo
object.
CrmBasedUserListInfo
contains three fields:
app_id
: A string that uniquely identifies the mobile application from which the data was collected. This is required when creatingCrmBasedUserList
for 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 Match userlists.data_source_type
: The data source of the list. The default value isFIRST_PARTY
. Allowlisted customers may create third-party sourced Customer Match lists.
The membership_life_span
attribute of the user list lets you define the
time period, in days, for which a user is considered to be in the list. The
membership_status
attribute defines whether the list accepts new users.
Customer Match user list types allow you to set membership_life_span
to 10000
to indicate no expiration.
private void runExample(GoogleAdsClient googleAdsClient, long customerId) {
// Creates a user list.
UserList userList =
UserList.newBuilder()
.setName(
StringValue.of(
"Contact Info Customer Match UserList example #" + System.currentTimeMillis()))
.setDescription(StringValue.of("Customer Match UserList containing contact info."))
.setMembershipStatus(UserListMembershipStatus.OPEN)
.setMembershipLifeSpan(Int64Value.of(365))
.setCrmBasedUserList(
CrmBasedUserListInfo.newBuilder()
// Sets the upload key type to CONTACT_INFO to upload an address, hashed email or
// hashed phone number.
.setUploadKeyType(CustomerMatchUploadKeyType.CONTACT_INFO)
.build())
.build();
// Creates the operation.
UserListOperation operation = UserListOperation.newBuilder().setCreate(userList).build();
// Creates the user list service client.
try (UserListServiceClient userListServiceClient =
googleAdsClient.getLatestVersion().createUserListServiceClient()) {
// Adds the user list.
MutateUserListsResponse response =
userListServiceClient.mutateUserLists(
Long.toString(customerId), ImmutableList.of(operation));
String userListResourceName = response.getResults(0).getResourceName();
// Prints the result.
System.out.printf("Created user list with resource name '%s'.%n", userListResourceName);
}
}
Creating an UploadUserDataRequest instance
Once you have created your Customer Match user list, you can populate it with
user data using the UserDataService
. The
UserDataService
contains the UploadUserData
method, which accepts an
UploadUserDataRequest
. In addition to
the customer_id
, the UploadUserDataRequest
accepts a list of the operations
to create the contacts and a required field called
customer_match_user_list_metadata
, which is populated with the resource name
of the remarketing list to target.
Begin by creating an UploadUserDataRequest
instance in which you will populate
the customer_id
and customer_match_user_list_metadata
:
// Creates a request to add user data operations to the user list based on email addresses.
String userListResourceName = ResourceNames.userList(customerId, userListId);
UploadUserDataRequest.Builder uploadUserDataRequest =
UploadUserDataRequest.newBuilder()
.setCustomerId(String.valueOf(customerId))
.setCustomerMatchUserListMetadata(
CustomerMatchUserListMetadata.newBuilder()
.setUserList(StringValue.of(userListResourceName))
.build());
Adding user contact information
You must now add user data to your user list. Keep in mind that each user list can only contain a single type of user data.
Adding customer contact information
To upload user contact information, set CrmBasedUserListInfo.upload_key_type
to CONTACT_INFO
.
First, add the operations to the UploadUserDataRequest
object. Each operation
contains a create
field populated with UserData
objects that hold one or more UserIdentifier
instances. Each UserIdentifier
contains one piece of identifying information
or can be one of several different types, each of which is described below.
To upload customer email addresses, create a new
UserDataOperation
and populate its create
field with a UserData
object. The UserData object accepts a list of
user_identifiers
. Populate the hashed_email
field with the customer email
addresses.
ImmutableList<String> EMAILS =
ImmutableList.of("client1@example.com", "client2@example.com", " Client3@example.com ");
// Hash normalized email addresses based on SHA-256 hashing algorithm.
List<UserDataOperation> userDataOperations = new ArrayList<>(EMAILS.size());
for (String email : EMAILS) {
UserDataOperation userDataOperation =
UserDataOperation.newBuilder()
.setCreate(
UserData.newBuilder()
.addUserIdentifiers(
UserIdentifier.newBuilder()
.setHashedEmail(StringValue.of(toSHA256String(email)))
.build())
.build())
.build();
userDataOperations.add(userDataOperation);
}
uploadUserDataRequest.addAllOperations(userDataOperations);
Uploading user address_info
is similar to uploading user email addresses.
However, instead of passing a hashed_email
, populate the address_info
field
with an OfflineUserAddressInfo
object
containing the user’s first_name
, last_name
, country_code
, and
postal_code
. Like email addresses, first_name
and last_name
are considered
personally identifiable information and must be hashed before uploading.
String firstName = "John";
String lastName = "Doe";
String countryCode = "US";
String postalCode = "10011";
UserIdentifier userIdentifierWithAddress =
UserIdentifier.newBuilder()
.setAddressInfo(
OfflineUserAddressInfo.newBuilder()
// First and last name must be normalized and hashed.
.setHashedFirstName(
StringValue.of(toSHA256String(firstName)))
.setHashedLastName(StringValue.of(toSHA256String(lastName)))
// Country code and zip code are sent in plaintext.
.setCountryCode(StringValue.of(countryCode))
.setPostalCode(StringValue.of(postalCode))
.build())
.build();
UserDataOperation userDataOperation =
UserDataOperation.newBuilder()
.setCreate(
UserData.newBuilder()
.addUserIdentifiers(userIdentifierWithAddress)
.build())
.build();
uploadUserDataRequest.addOperations(userDataOperation);
Adding mobile IDs
To upload user information matched from mobile advertising IDs, set
CrmBasedUserListInfo.upload_key_type
to MOBILE_ADVERTISING_ID
.
Uploading a mobile ID to a UserList
is done in the same way as for contact
information. However, you must populate the mobile_id
field of the
UserIdentifier
object instead of populating the user’s contact information.
ImmutableList<String> MOBILE_IDS =
ImmutableList.of("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
" YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY ");
// Creates operations with mobile Ids.
List<UserDataOperation> userDataOperations = new ArrayList<>(MOBILE_IDS.size());
for (String mobileId : MOBILE_IDS) {
UserDataOperation userDataOperation =
UserDataOperation.newBuilder()
.setCreate(
UserData.newBuilder()
.addUserIdentifiers(
UserIdentifier.newBuilder()
.setMobileId(StringValue.of(toNormalizedString(mobileId)))
.build())
.build())
.build();
userDataOperations.add(userDataOperation);
}
uploadUserDataRequest.addAllOperations(userDataOperations);
Adding CRM IDs
To populate the Customer Match user list with CRM IDs, set
CrmBasedUserListInfo.upload_key_type
to CRM_ID
. CRM IDs are matched from a
user ID generated and assigned by the advertiser. This is similar to uploading
MOBILE_ADVERTISING_ID
instances, but you will instead populate the
third_party_user_id
field of the UserIdentifier
object.
ImmutableList<String> CRM_IDS = ImmutableList.of("exampleCrmId1", " exampleCrmId2 ");
// Creates operations with CRM Ids.
List<UserDataOperation> userDataOperations = new ArrayList<>(CRM_IDS.size());
for (String crmId : CRM_IDS) {
UserDataOperation userDataOperation =
UserDataOperation.newBuilder()
.setCreate(
UserData.newBuilder()
.addUserIdentifiers(
UserIdentifier.newBuilder()
.setThirdPartyUserId(StringValue.of(toNormalizedString(crmId)))
.build())
.build())
.build();
userDataOperations.add(userDataOperation);
}
uploadUserDataRequest.addAllOperations(userDataOperations);
Encoding user data
As outlined in the About the customer matching process support article, private customer data must be hashed before uploading it to a user list. Values should be normalized (trimmed of whitespace and converted to lowercase) before hashing. Normalized data values should then be hashed using the SHA256 algorithm.
/**
* Hashes a string using SHA-256 hashing algorithm.
*
* @param str the string to hash.
* @return the SHA-256 hash string representation.
* @throws UnsupportedEncodingException If UTF-8 charset is not supported.
*/
private static String toSHA256String(String str) throws UnsupportedEncodingException {
MessageDigest digest = getSHA256MessageDigest();
byte[] hash = digest.digest(toNormalizedString(str).getBytes("UTF-8"));
StringBuilder result = new StringBuilder();
for (byte b : hash) {
result.append(String.format("%02x", b));
}
return result.toString();
}
/** Returns SHA-256 hashing algorithm. */
private static MessageDigest getSHA256MessageDigest() {
try {
return MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Missing SHA-256 algorithm implementation.", e);
}
}
/**
* Removes leading and trailing whitespace and converts all characters to lowercase.
*
* @param value the string to normalize.
* @return a normalized copy of the string.
*/
private static String toNormalizedString(String value) {
return value.trim().toLowerCase();
}
Sending the request
After adding operations to the UploadUserDataRequest
instance, call the
uploadUserData
method on the UserDataServiceClient
to send the request to
the Google Ads API server. You can see that the request was successful by
getting the operations count and upload time in the response object. Keep in
mind that it may take several hours for the list to be populated with members.
// Creates the user data service client.
try (UserDataServiceClient userDataServiceClient =
googleAdsClient.getLatestVersion().createUserDataServiceClient()) {
// Add operations to the user list based on the user data type.
UploadUserDataResponse response =
userDataServiceClient.uploadUserData(uploadUserDataRequest.build());
// Displays the results.
// Reminder: it may take several hours for the list to be populated with members.
System.out.printf(
"Received %d operations at %s",
response.getReceivedOperationsCount().getValue(),
response.getUploadDateTime().getValue());
}
Creating a Customer Match UserList using OfflineDataJobService
The example below demonstrates creating a new Customer Match user list and
populating it using the OfflineUserDataJobService
. Note that this service
behaves similarly to UserDataService
; see the text above for example code that
utilizes UserDataService
.
Java
private void addUsersToCustomerMatchUserList( GoogleAdsClient googleAdsClient, long customerId, boolean runJob, String userListResourceName, Long offlineUserDataJobId) throws UnsupportedEncodingException { try (OfflineUserDataJobServiceClient offlineUserDataJobServiceClient = googleAdsClient.getLatestVersion().createOfflineUserDataJobServiceClient()) { String offlineUserDataJobResourceName; if (offlineUserDataJobId == null) { // Creates a new offline user data job. OfflineUserDataJob offlineUserDataJob = OfflineUserDataJob.newBuilder() .setType(OfflineUserDataJobType.CUSTOMER_MATCH_USER_LIST) .setCustomerMatchUserListMetadata( CustomerMatchUserListMetadata.newBuilder().setUserList(userListResourceName)) .build(); // Issues a request to create the offline user data job. CreateOfflineUserDataJobResponse createOfflineUserDataJobResponse = offlineUserDataJobServiceClient.createOfflineUserDataJob( Long.toString(customerId), offlineUserDataJob); offlineUserDataJobResourceName = createOfflineUserDataJobResponse.getResourceName(); System.out.printf( "Created an offline user data job with resource name: %s.%n", offlineUserDataJobResourceName); } else { // Reuses the specified offline user data job. offlineUserDataJobResourceName = ResourceNames.offlineUserDataJob(customerId, offlineUserDataJobId); } // Issues a request to add the operations to the offline user data job. This example // only adds a few operations, so it only sends one AddOfflineUserDataJobOperations request. // If your application is adding a large number of operations, split the operations into // batches and send multiple AddOfflineUserDataJobOperations requests for the SAME job. See // https://developers.google.com/google-ads/api/docs/remarketing/audience-types/customer-match#customer_match_considerations // and https://developers.google.com/google-ads/api/docs/best-practices/quotas#user_data // for more information on the per-request limits. List<OfflineUserDataJobOperation> userDataJobOperations = buildOfflineUserDataJobOperations(); AddOfflineUserDataJobOperationsResponse response = offlineUserDataJobServiceClient.addOfflineUserDataJobOperations( AddOfflineUserDataJobOperationsRequest.newBuilder() .setResourceName(offlineUserDataJobResourceName) .setEnablePartialFailure(true) .addAllOperations(userDataJobOperations) .build()); // Prints the status message if any partial failure error is returned. // NOTE: The details of each partial failure error are not printed here, you can refer to // the example HandlePartialFailure.java to learn more. if (response.hasPartialFailureError()) { GoogleAdsFailure googleAdsFailure = ErrorUtils.getInstance().getGoogleAdsFailure(response.getPartialFailureError()); System.out.printf( "Encountered %d partial failure errors while adding %d operations to the offline user " + "data job: '%s'. Only the successfully added operations will be executed when " + "the job runs.%n", googleAdsFailure.getErrorsCount(), userDataJobOperations.size(), response.getPartialFailureError().getMessage()); } else { System.out.printf( "Successfully added %d operations to the offline user data job.%n", userDataJobOperations.size()); } if (!runJob) { System.out.printf( "Not running offline user data job '%s', as requested.%n", offlineUserDataJobResourceName); return; } // Issues an asynchronous request to run the offline user data job for executing // all added operations. offlineUserDataJobServiceClient.runOfflineUserDataJobAsync(offlineUserDataJobResourceName); // BEWARE! The above call returns an OperationFuture. The execution of that future depends on // the thread pool which is owned by offlineUserDataJobServiceClient. If you use this future, // you *must* keep the service client in scope too. // See https://developers.google.com/google-ads/api/docs/client-libs/java/lro for more detail. // Offline user data jobs may take 6 hours or more to complete, so instead of waiting for the // job to complete, retrieves and displays the job status once. If the job is completed // successfully, prints information about the user list. Otherwise, prints the query to use // to check the job again later. checkJobStatus(googleAdsClient, customerId, offlineUserDataJobResourceName); } }
C#
private static string AddUsersToCustomerMatchUserList(GoogleAdsClient client, long customerId, string userListResourceName, bool runJob, long? offlineUserDataJobId) { // Get the OfflineUserDataJobService. OfflineUserDataJobServiceClient service = client.GetService( Services.V13.OfflineUserDataJobService); string offlineUserDataJobResourceName; if (offlineUserDataJobId == null) { // Creates a new offline user data job. OfflineUserDataJob offlineUserDataJob = new OfflineUserDataJob() { Type = OfflineUserDataJobType.CustomerMatchUserList, CustomerMatchUserListMetadata = new CustomerMatchUserListMetadata() { UserList = userListResourceName } }; // Issues a request to create the offline user data job. CreateOfflineUserDataJobResponse response1 = service.CreateOfflineUserDataJob( customerId.ToString(), offlineUserDataJob); offlineUserDataJobResourceName = response1.ResourceName; Console.WriteLine($"Created an offline user data job with resource name: " + $"'{offlineUserDataJobResourceName}'."); } else { // Reuses the specified offline user data job. offlineUserDataJobResourceName = ResourceNames.OfflineUserDataJob(customerId, offlineUserDataJobId.Value); } AddOfflineUserDataJobOperationsRequest request = new AddOfflineUserDataJobOperationsRequest() { ResourceName = offlineUserDataJobResourceName, Operations = { BuildOfflineUserDataJobOperations() }, EnablePartialFailure = true, }; // Issues a request to add the operations to the offline user data job. This example // only adds a few operations, so it only sends one AddOfflineUserDataJobOperations // request. // If your application is adding a large number of operations, split the operations into // batches and send multiple AddOfflineUserDataJobOperations requests for the SAME job. // See https://developers.google.com/google-ads/api/docs/remarketing/audience-types/customer-match#customer_match_considerations // and https://developers.google.com/google-ads/api/docs/best-practices/quotas#user_data // for more information on the per-request limits. AddOfflineUserDataJobOperationsResponse response2 = service.AddOfflineUserDataJobOperations(request); // Prints the status message if any partial failure error is returned. // Note: The details of each partial failure error are not printed here, // you can refer to the example HandlePartialFailure.cs to learn more. if (response2.PartialFailureError != null) { // Extracts the partial failure from the response status. GoogleAdsFailure partialFailure = response2.PartialFailure; Console.WriteLine($"{partialFailure.Errors.Count} partial failure error(s) " + $"occurred"); } Console.WriteLine("The operations are added to the offline user data job."); if (!runJob) { Console.WriteLine($"Not running offline user data job " + "'{offlineUserDataJobResourceName}', as requested."); return offlineUserDataJobResourceName; } // Issues an asynchronous request to run the offline user data job for executing // all added operations. Operation<Empty, OfflineUserDataJobMetadata> operationResponse = service.RunOfflineUserDataJob(offlineUserDataJobResourceName); Console.WriteLine("Asynchronous request to execute the added operations started."); // Since offline user data jobs may take 24 hours or more to complete, it may not be // practical to do operationResponse.PollUntilCompleted() to wait for the results. // Instead, we save the offlineUserDataJobResourceName and use GoogleAdsService.Search // to check for the job status periodically. // In case you wish to follow the PollUntilCompleted or PollOnce approach, make sure // you keep both operationResponse and service variables alive until the polling // completes. return offlineUserDataJobResourceName; }
PHP
private static function addUsersToCustomerMatchUserList( GoogleAdsClient $googleAdsClient, int $customerId, bool $runJob, ?string $userListResourceName, ?int $offlineUserDataJobId ) { $offlineUserDataJobResourceName = null; $offlineUserDataJobServiceClient = $googleAdsClient->getOfflineUserDataJobServiceClient(); if (is_null($offlineUserDataJobId)) { // Creates a new offline user data job. $offlineUserDataJob = new OfflineUserDataJob([ 'type' => OfflineUserDataJobType::CUSTOMER_MATCH_USER_LIST, 'customer_match_user_list_metadata' => new CustomerMatchUserListMetadata([ 'user_list' => $userListResourceName ]) ]); // Issues a request to create the offline user data job. /** @var CreateOfflineUserDataJobResponse $createOfflineUserDataJobResponse */ $createOfflineUserDataJobResponse = $offlineUserDataJobServiceClient->createOfflineUserDataJob( $customerId, $offlineUserDataJob ); $offlineUserDataJobResourceName = $createOfflineUserDataJobResponse->getResourceName(); printf( "Created an offline user data job with resource name: '%s'.%s", $offlineUserDataJobResourceName, PHP_EOL ); } else { // Reuses the specified offline user data job. $offlineUserDataJobResourceName = ResourceNames::forOfflineUserDataJob($customerId, $offlineUserDataJobId); } // Issues a request to add the operations to the offline user data job. This example // only adds a few operations, so it only sends one AddOfflineUserDataJobOperations request. // If your application is adding a large number of operations, split the operations into // batches and send multiple AddOfflineUserDataJobOperations requests for the SAME job. See // https://developers.google.com/google-ads/api/docs/remarketing/audience-types/customer-match#customer_match_considerations // and https://developers.google.com/google-ads/api/docs/best-practices/quotas#user_data // for more information on the per-request limits. /** @var AddOfflineUserDataJobOperationsResponse $operationResponse */ $response = $offlineUserDataJobServiceClient->addOfflineUserDataJobOperations( $offlineUserDataJobResourceName, self::buildOfflineUserDataJobOperations(), ['enablePartialFailure' => true] ); // Prints the status message if any partial failure error is returned. // Note: The details of each partial failure error are not printed here, you can refer to // the example HandlePartialFailure.php to learn more. if ($response->hasPartialFailureError()) { // Extracts the partial failure from the response status. $partialFailure = GoogleAdsFailures::fromAny( $response->getPartialFailureError()->getDetails()->getIterator()->current() ); printf( "%d partial failure error(s) occurred: %s.%s", count($partialFailure->getErrors()), $response->getPartialFailureError()->getMessage(), PHP_EOL ); } else { print 'The operations are added to the offline user data job.' . PHP_EOL; } if ($runJob === false) { printf( "Not running offline user data job '%s', as requested.%s", $offlineUserDataJobResourceName, PHP_EOL ); return; } // Issues an asynchronous request to run the offline user data job for executing all added // operations. The result is OperationResponse. Visit the OperationResponse.php file for // more details. $offlineUserDataJobServiceClient->runOfflineUserDataJob($offlineUserDataJobResourceName); // Offline user data jobs may take 6 hours or more to complete, so instead of waiting // for the job to complete, retrieves and displays the job status once. If the job is // completed successfully, prints information about the user list. Otherwise, prints the // query to use to check the job again later. self::checkJobStatus($googleAdsClient, $customerId, $offlineUserDataJobResourceName); }
Python
def add_users_to_customer_match_user_list( client, customer_id, user_list_resource_name, run_job, offline_user_data_job_id, ): """Uses Customer Match to create and add users to a new user list. Args: client: The Google Ads client. customer_id: The ID for the customer that owns the user list. user_list_resource_name: The resource name of the user list to which to add users. run_job: If true, runs the OfflineUserDataJob after adding operations. Otherwise, only adds operations to the job. offline_user_data_job_id: ID of an existing OfflineUserDataJob in the PENDING state. If None, a new job is created. """ # Creates the OfflineUserDataJobService client. offline_user_data_job_service_client = client.get_service( "OfflineUserDataJobService" ) if offline_user_data_job_id: # Reuses the specified offline user data job. offline_user_data_job_resource_name = offline_user_data_job_service_client.offline_user_data_job_path( customer_id, offline_user_data_job_id ) else: # Creates a new offline user data job. offline_user_data_job = client.get_type("OfflineUserDataJob") offline_user_data_job.type_ = ( client.enums.OfflineUserDataJobTypeEnum.CUSTOMER_MATCH_USER_LIST ) offline_user_data_job.customer_match_user_list_metadata.user_list = ( user_list_resource_name ) # Issues a request to create an offline user data job. create_offline_user_data_job_response = offline_user_data_job_service_client.create_offline_user_data_job( customer_id=customer_id, job=offline_user_data_job ) offline_user_data_job_resource_name = ( create_offline_user_data_job_response.resource_name ) print( "Created an offline user data job with resource name: " f"'{offline_user_data_job_resource_name}'." ) # Issues a request to add the operations to the offline user data job. # Best Practice: This example only adds a few operations, so it only sends # one AddOfflineUserDataJobOperations request. If your application is adding # a large number of operations, split the operations into batches and send # multiple AddOfflineUserDataJobOperations requests for the SAME job. See # https://developers.google.com/google-ads/api/docs/remarketing/audience-types/customer-match#customer_match_considerations # and https://developers.google.com/google-ads/api/docs/best-practices/quotas#user_data # for more information on the per-request limits. request = client.get_type("AddOfflineUserDataJobOperationsRequest") request.resource_name = offline_user_data_job_resource_name request.operations = build_offline_user_data_job_operations(client) request.enable_partial_failure = True # Issues a request to add the operations to the offline user data job. response = offline_user_data_job_service_client.add_offline_user_data_job_operations( request=request ) # Prints the status message if any partial failure error is returned. # Note: the details of each partial failure error are not printed here. # Refer to the error_handling/handle_partial_failure.py example to learn # more. # Extracts the partial failure from the response status. partial_failure = getattr(response, "partial_failure_error", None) if getattr(partial_failure, "code", None) != 0: error_details = getattr(partial_failure, "details", []) for error_detail in error_details: failure_message = client.get_type("GoogleAdsFailure") # Retrieve the class definition of the GoogleAdsFailure instance # in order to use the "deserialize" class method to parse the # error_detail string into a protobuf message object. failure_object = type(failure_message).deserialize( error_detail.value ) for error in failure_object.errors: print( "A partial failure at index " f"{error.location.field_path_elements[0].index} occurred.\n" f"Error message: {error.message}\n" f"Error code: {error.error_code}" ) print("The operations are added to the offline user data job.") if not run_job: print( "Not running offline user data job " f"'{offline_user_data_job_resource_name}', as requested." ) return # Issues a request to run the offline user data job for executing all # added operations. offline_user_data_job_service_client.run_offline_user_data_job( resource_name=offline_user_data_job_resource_name ) # Retrieves and displays the job status. check_job_status(client, customer_id, offline_user_data_job_resource_name)
Ruby
def add_users_to_customer_match_user_list(client, customer_id, run_job, user_list, job_id) offline_user_data_service = client.service.offline_user_data_job job_name = if job_id.nil? # Creates the offline user data job. offline_user_data_job = client.resource.offline_user_data_job do |job| job.type = :CUSTOMER_MATCH_USER_LIST job.customer_match_user_list_metadata = client.resource.customer_match_user_list_metadata do |m| m.user_list = user_list end end # Issues a request to create the offline user data job. response = offline_user_data_service.create_offline_user_data_job( customer_id: customer_id, job: offline_user_data_job, ) offline_user_data_job_resource_name = response.resource_name puts "Created an offline user data job with resource name: " \ "#{offline_user_data_job_resource_name}" offline_user_data_job_resource_name else client.path.offline_user_data_job(customer_id, job_id) end # Issues a request to add the operations to the offline user data job. This # example only adds a few operations, so it only sends one # AddOfflineUserDataJobOperations request. If your application is adding a # large number of operations, split the operations into batches and send # multiple AddOfflineUserDataJobOperations requests for the SAME job. See # https://developers.google.com/google-ads/api/docs/remarketing/audience-types/customer-match#customer_match_considerations # and https://developers.google.com/google-ads/api/docs/best-practices/quotas#user_data # for more information on the per-request limits. response = offline_user_data_service.add_offline_user_data_job_operations( resource_name: offline_user_data_job_resource_name, enable_partial_failure: true, operations: build_offline_user_data_job_operations(client), ) # Prints errors if any partial failure error is returned. if response.partial_failure_error failures = client.decode_partial_failure_error(response.partial_failure_error) failures.each do |failure| failure.errors.each do |error| human_readable_error_path = error .location .field_path_elements .map { |location_info| if location_info.index "#{location_info.field_name}[#{location_info.index}]" else "#{location_info.field_name}" end }.join(" > ") errmsg = "error occured while adding operations " \ "#{human_readable_error_path}" \ " with value: #{error.trigger.string_value}" \ " because #{error.message.downcase}" puts errmsg end end end puts "The operations are added to the offline user data job." unless run_job puts "Not running offline user data job #{job_name}, as requested." return end # Issues an asynchronous request to run the offline user data job # for executing all added operations. response = offline_user_data_service.run_offline_user_data_job( resource_name: offline_user_data_job_resource_name ) puts "Asynchronous request to execute the added operations started." puts "Waiting until operation completes." # Offline user data jobs may take 6 hours or more to complete, so instead of # waiting for the job to complete, retrieves and displays the job status # once. If the job is completed successfully, prints information about the # user list. Otherwise, prints the query to use to check the job again later. check_job_status( client, customer_id, offline_user_data_job_resource_name, ) end
Perl
sub add_users_to_customer_match_user_list { my ($api_client, $customer_id, $run_job, $user_list_resource_name, $offline_user_data_job_id) = @_; my $offline_user_data_job_service = $api_client->OfflineUserDataJobService(); my $offline_user_data_job_resource_name = undef; if (!defined $offline_user_data_job_id) { # Create a new offline user data job. my $offline_user_data_job = Google::Ads::GoogleAds::V13::Resources::OfflineUserDataJob->new({ type => CUSTOMER_MATCH_USER_LIST, customerMatchUserListMetadata => Google::Ads::GoogleAds::V13::Common::CustomerMatchUserListMetadata-> new({ userList => $user_list_resource_name })}); # Issue a request to create the offline user data job. my $create_offline_user_data_job_response = $offline_user_data_job_service->create({ customerId => $customer_id, job => $offline_user_data_job }); $offline_user_data_job_resource_name = $create_offline_user_data_job_response->{resourceName}; printf "Created an offline user data job with resource name: '%s'.\n", $offline_user_data_job_resource_name; } else { # Reuse the specified offline user data job. $offline_user_data_job_resource_name = Google::Ads::GoogleAds::V13::Utils::ResourceNames::offline_user_data_job( $customer_id, $offline_user_data_job_id); } # Issue a request to add the operations to the offline user data job. # This example only adds a few operations, so it only sends one AddOfflineUserDataJobOperations # request. If your application is adding a large number of operations, split # the operations into batches and send multiple AddOfflineUserDataJobOperations # requests for the SAME job. See # https://developers.google.com/google-ads/api/docs/remarketing/audience-types/customer-match#customer_match_considerations # and https://developers.google.com/google-ads/api/docs/best-practices/quotas#user_data # for more information on the per-request limits. my $user_data_job_operations = build_offline_user_data_job_operations(); my $response = $offline_user_data_job_service->add_operations( { resourceName => $offline_user_data_job_resource_name, enablePartialFailure => "true", operations => $user_data_job_operations }); # Print the status message if any partial failure error is returned. # Note: The details of each partial failure error are not printed here, you can # refer to the example handle_partial_failure.pl to learn more. if ($response->{partialFailureError}) { # Extract the partial failure from the response status. my $partial_failure = $response->{partialFailureError}{details}[0]; printf "Encountered %d partial failure errors while adding %d operations " . "to the offline user data job: '%s'. Only the successfully added " . "operations will be executed when the job runs.\n", scalar @{$partial_failure->{errors}}, scalar @$user_data_job_operations, $response->{partialFailureError}{message}; } else { printf "Successfully added %d operations to the offline user data job.\n", scalar @$user_data_job_operations; } if (!defined $run_job) { print "Not running offline user data job $offline_user_data_job_resource_name, as requested.\n"; return; } # Issue an asynchronous request to run the offline user data job for executing # all added operations. my $operation_response = $offline_user_data_job_service->run({ resourceName => $offline_user_data_job_resource_name }); # Offline user data jobs may take 6 hours or more to complete, so instead of waiting # for the job to complete, this example retrieves and displays the job status once. # If the job is completed successfully, it prints information about the user list. # Otherwise, it prints, the query to use to check the job status again later. check_job_status($api_client, $customer_id, $offline_user_data_job_resource_name); }