开始使用目标客户匹配

借助目标客户匹配功能,您可以利用您的线上及线下数据,在搜索网络、“购物”标签页、Gmail、YouTube 和展示广告网络上覆盖和重新吸引客户。目标客户匹配功能利用客户与您分享的信息,将广告定位到相应客户及其他类似客户。您可以 批量上传客户关系管理 (CRM) 数据、添加或移除 数据,或者使用这些用户名单创建 logical_user_list。

如需查看不同类型的受众群体 细分,并将目标客户匹配与其他用户名单选项进行比较,请参阅受众群体管理概览。

详细了解目标客户匹配和受众群体定位。

前提条件

并非所有账号都有资格使用目标客户匹配功能。要使用目标客户匹配功能,您的账号必须符合以下要求:

  • 政策遵从记录良好
  • 付款记录良好

广告客户可以使用哪些功能取决于其账号满足哪些要求。如需了解资格要求 和限制,请参阅目标客户匹配政策。

开始之前:规划实施方案

在开始编写任何代码之前,请务必规划目标客户匹配功能的实施方案。本部分概述了整个流程和行动要点,可帮助您设计稳健的集成方案。制定明确的计划后,您就可以继续执行后续的实施步骤。

使用流程

以下是创建客户名单并进行定位的推荐流程:

  1. 创建空的客户名单。

  2. 创建 OfflineUserDataJob。创建一个大型作业比创建多个小型作业效率高得多。

    确保在 OfflineUserDataJob create 请求中填充 customer_match_user_list_metadata 的consent 字段。对于 remove 请求,无需征得用户同意。如果作业的 consent.ad_user_data 或 consent.ad_personalization 设置为 DENIED,则 API 返回 OfflineUserDataJobError.CUSTOMER_NOT_ACCEPTED_CUSTOMER_DATA_TERMS。

    如果用户拒绝提供同意,您可以创建一个包含 remove 操作的作业,以从用户名单中移除用户的标识符。

    如果您缺少特定用户的同意,请创建一个单独的作业,在该作业中,您无需设置作业的 customer_match_user_list_metadata 的 consent 字段,然后使用该单独作业的 create 操作为这些用户添加标识符。

  3. 使用 OfflineUserDataJobService.AddOfflineUserDataJobOperations方法添加操作。我们建议您在一次调用中添加最多 10,000 个标识符,以实现最佳处理效果。单个 AddOfflineUserDataJobOperations 请求最多可包含 100,000 个标识符,这些标识符分布在操作列表中的所有 UserData 对象中。

    例如,如果您的每个 UserData 对象都有一个 hashed_email 的 UserIdentifier 和另一个 hashed_phone_number 的 UserIdentifier,那么每个请求发送 5,000 个 UserData 对象是最佳做法,因为每个请求总共包含 10,000 个用户标识符。

  4. 重复上一步,直到添加完所有操作或作业达到容量上限。您可以向单个作业添加的操作数量没有限制;不过,我们建议每个作业的操作数量不超过 1,000,000 个,以实现最佳处理效果。

  5. 运行作业。您必须在创建作业后的 5 天内运行该作业。 否则,该作业将无法再运行。

  6. 轮询以确定上传是否成功。

  7. 验证匹配率。

  8. 定位到名单。

OfflineUserDataJobService 和 UserDataService

您可以使用两种服务上传目标客户匹配数据。请根据您的使用场景选择服务,因为服务可能会受到限制。

目标客户匹配数据上传服务
OfflineUserDataJobService (首选) 大多数开发者都使用此服务。此服务针对高吞吐量的大规模上传进行了优化,并在完成后返回成功指标。本指南 主要介绍此服务。
UserDataService 此服务经过优化,可一次上传少量标识符并进行零星更新 ,但未针对持续运行进行优化。每个请求的操作数量上限为 10 个。此外,单个请求的所有 user_identifiers 中包含的项总数不能超过 100 个。

如需了解如何使用此服务进行上传,请访问 管理目标客户匹配集成指南。

您应在 UploadUserDataRequest create 请求中填充 customer_match_user_list_metadata 的 consent 字段。对于 remove 请求,无需征得用户同意。如果 consent.ad_user_data 或 consent.ad_personalization 为 DENIED,则 API 会返回一个 received_operations_count 设置为零的响应,表示请求未得到处理。如果标识符的同意状态为 DENIED,则您可以使用 remove 的 UploadUserDataRequest 操作请求从用户名单中移除该标识符。

最佳做法

设计目标客户匹配集成时,请牢记以下最佳做法:

  • 请勿尝试使用多个账号修改单个用户名单。用户 名单只能由创建该名单的 Google Ads 账号或 数据合作伙伴账号进行修改。

  • 尽可能增加每个 AddOfflineUserDataJobOperationsRequest的操作数量(最多 100,000 个标识符),以 避免 RESOURCE_EXHAUSTED 错误。

  • 请勿在同一 OfflineUserDataJob中混用create和remove操作。否则可能会导致 CONFLICTING_OPERATION 错误。

  • 在 AddOfflineUserDataJobOperationsRequest中启用partial_failure,以便在运行作业之前检测到任何 存在问题的操作。操作在 上传到 OfflineUserDataJob时会经过验证。

  • 如需防止在更新用户名单时出现 CONCURRENT_MODIFICATION 错误,请执行以下操作:

    避免同时有多个 OfflineUserDataJob 进程处于 RUNNING 状态,如果它们定位到同一用户名单资源名称( 在 CustomerMatchUserListMetadata.user_list 中指定)。出现此错误的原因是多个作业尝试同时修改同一名单。如果您在 API 作业正在处理时通过 Google Ads 界面修改名单,也可能会出现此冲突。

    此限制适用于已启动(即处于 RUNNING 状态)的作业。相比之下,您可以安全地发出多个 AddOfflineUserDataJobOperations 请求,以便在单个 OfflineUserDataJob 仍处于 PENDING 状态时向其添加数据。 您可以在调用 RunOfflineUserDataJob以启动该进程之前的任何时间向作业添加操作。

  • 如果您有数千个操作要应用,请创建一个包含所有操作的 OfflineUserDataJob。 请勿创建多个作业,每个作业仅包含数百个操作,然后按顺序或同时运行这些作业。包含所有操作的单个大型作业比多个小型作业效率高得多,并且可以减少工作流中出现错误的可能性。

如需了解如何使用客户名单实现最佳定位,请参阅 目标客户匹配最佳实践。

第 1 步:创建客户名单

使用 UserListService 创建客户名单。客户名单是 通过在 user_list 对象上设置 crm_based_user_list 字段来创建的。可以在支持定位客户名单的广告系列类型上设置 crm_based_user_list 字段:

不同广告系列类型中的目标客户匹配
搜索网络 广告在 Google 搜索网络上展示。
展示广告网络 广告在 Google 展示广告网络上展示。
将展示广告网络也纳入投放范围的搜索广告系列 广告在 Google 搜索网络和 Google 展示广告网络上展示。
需求开发广告系列 广告在 YouTube、Google 探索和 Gmail 中展示(使用 Audience 资源进行定位,该资源位于 AdGroupCriterion.audience)。
效果最大化广告系列 广告在所有 Google 渠道中展示(用作受众群体信号,位于 AssetGroupSignal.audience)。
视频广告系列 广告在 YouTube 上展示。
购物广告系列 广告在“购物”标签页中展示。

crm_based_user_list 包含三个字段:

  • app_id:一个字符串,用于唯一标识收集数据的移动应用。为上传移动广告 ID 创建 CrmBasedUserList 时,必须提供此字段。

  • upload_key_type:名单的匹配键类型,可以是 CONTACT_INFO、CRM_ID 或 MOBILE_ADVERTISING_ID。同一名单中不允许使用混合数据类型。所有客户名单都必须提供此字段。

  • data_source_type:名单的数据源。默认值为 FIRST_PARTY。许可清单中的客户可以创建第三方来源的客户名单(THIRD_PARTY_CREDIT_BUREAU、THIRD_PARTY_VOTER_FILE 或 THIRD_PARTY_PARTNER_DATA)。只有 FIRST_PARTY 来源的名单可以与其他名单组合。

借助用户名单的 membership_life_span 属性,您可以定义用户被视为在名单中的 时间段(以天为单位)。目标客户匹配用户名单的 membership_life_span 不得超过 540,这也是默认值。

The membership_status 属性用于定义名单是否接受新 用户。

用于创建客户名单的代码示例

Java

private String createCustomerMatchUserList(GoogleAdsClient googleAdsClient, long customerId) {
  // Creates the new user list.
  UserList userList =
      UserList.newBuilder()
          .setName("Customer Match list #" + getPrintableDateTime())
          .setDescription("A list of customers that originated from email 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.
          .setMembershipLifeSpan(30)
          // Sets 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.
          .setCrmBasedUserList(
              CrmBasedUserListInfo.newBuilder()
                  .setUploadKeyType(CustomerMatchUploadKeyType.CONTACT_INFO))
          .build();

  // Creates the operation.
  UserListOperation operation = UserListOperation.newBuilder().setCreate(userList).build();

  // Creates the service client.
  try (UserListServiceClient userListServiceClient =
      googleAdsClient.getLatestVersion().createUserListServiceClient()) {
    // Adds the user list.
    MutateUserListsResponse response =
        userListServiceClient.mutateUserLists(
            Long.toString(customerId), ImmutableList.of(operation));
    // Prints the response.
    System.out.printf(
        "Created Customer Match user list with resource name: %s.%n",
        response.getResults(0).getResourceName());
    return response.getResults(0).getResourceName();
  }
}

      

C#

private string CreateCustomerMatchUserList(GoogleAdsClient client, long customerId)
{
    // Get the UserListService.
    UserListServiceClient service = client.GetService(Services.V25.UserListService);

    // Creates the user list.
    UserList userList = new UserList()
    {
        Name = $"Customer Match list# {ExampleUtilities.GetShortRandomString()}",
        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.
        MembershipLifeSpan = 30,
        CrmBasedUserList = new CrmBasedUserListInfo()
        {
            UploadKeyType = CustomerMatchUploadKeyType.ContactInfo
        }
    };
    // Creates the user list operation.
    UserListOperation operation = new UserListOperation()
    {
        Create = userList
    };

    // Issues a mutate request to add the user list and prints some information.
    MutateUserListsResponse response = service.MutateUserLists(
        customerId.ToString(), new[] { operation });
    string userListResourceName = response.Results[0].ResourceName;
    Console.WriteLine($"User list with resource name '{userListResourceName}' " +
        $"was created.");
    return userListResourceName;
}
      

PHP

private static function createCustomerMatchUserList(
    GoogleAdsClient $googleAdsClient,
    int $customerId
): string {
    // Creates the user list.
    $userList = new UserList(
        [
        'name' => 'Customer Match list #' . Helper::getPrintableDatetime(),
        '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.
        'membership_life_span' => 30,
        'crm_based_user_list' => new CrmBasedUserListInfo(
            [
            // Sets 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.
            'upload_key_type' => CustomerMatchUploadKeyType::CONTACT_INFO
            ]
        )
        ]
    );

    // Creates the user list operation.
    $operation = new UserListOperation();
    $operation->setCreate($userList);

    // Issues a mutate request to add the user list and prints some information.
    $userListServiceClient = $googleAdsClient->getUserListServiceClient();
    $response = $userListServiceClient->mutateUserLists(
        MutateUserListsRequest::build($customerId, [$operation])
    );
    $userListResourceName = $response->getResults()[0]->getResourceName();
    printf("User list with resource name '%s' was created.%s", $userListResourceName, PHP_EOL);

    return $userListResourceName;
}
      

Python

def create_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.create
    user_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 = 30

    response: 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_name
    print(
        f"User list with resource name '{user_list_resource_name}' was created."
    )

    return user_list_resource_name
      

Ruby

def create_customer_match_user_list(client, customer_id)
  # Creates the user list.
  operation = client.operation.create_resource.user_list do |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 = 30
    ul.crm_based_user_list = client.resource.crm_based_user_list_info do |crm|
      crm.upload_key_type = :CONTACT_INFO
    end
  end

  # 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_name
  puts "User list with resource name #{resource_name} was created."

  resource_name
end
      

Perl

sub create_customer_match_user_list {
  my ($api_client, $customer_id) = @_;

  # Create the user list.
  my $user_list = Google::Ads::GoogleAds::V25::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::V25::Common::CrmBasedUserListInfo->new({
          uploadKeyType => CONTACT_INFO
        })});

  # Create the user list operation.
  my $user_list_operation =
    Google::Ads::GoogleAds::V25::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;
}
      

curl

第 2 步:向用户名单添加成员

三个主要匹配键是:电子邮件地址、邮寄地址和电话号码。您可以使用用户 ID 和移动设备 ID 作为匹配键,但由于这些解决方案依赖于 Cookie 和设备 ID,因此不太能适应未来的变化。 我们建议您尽可能上传用户联系信息(例如电子邮件地址、邮寄地址和电话号码),而不是 CRM 或 Mobile ID。

每个用户名单只能包含一种客户数据,如 CrmBasedUserListInfo.upload_key_type字段所指定。此外,代表单个用户的 UserData 对象最多可包含 20 个 用户标识符,每个标识符都有自己的 UserIdentifier 对象。如果标识符超过 20 个,则会导致 TOO_MANY_USER_IDENTIFIERS 错误。

只有在广告投放时,目标客户匹配用户名单中的活跃用户数达到最低阈值时,Google Ads 才会使用该名单进行定位;活跃用户是指名单中在 Gmail、Google 搜索、YouTube 或展示广告网络上活跃的用户数。请至少上传 5,000 名成员,以增加获得足够多的匹配活跃用户进行定位的机会。

上传用户联系信息

如需上传用户电子邮件地址、邮寄地址或电话号码,请将 upload_key_type 设置为 CONTACT_INFO。联系信息必须与 Google 账号相关联才能进行匹配,并且无法定位企业账号(例如 Google Workspace)。

出于隐私问题方面的考虑,在上传电子邮件地址、名字、姓氏和电话号码之前,必须使用 SHA-256 算法对这些数据进行哈希处理。为了使哈希结果实现标准化,在对其中某个值进行哈希处理之前,请确保执行以下任务:

如果联系信息在哈希处理之前格式不正确,API 仍会接受哈希处理后的信息,但无法将其与客户匹配。

如果您要上传邮寄地址数据,则必须至少包含以下字段:

  • 国家/地区代码
  • 邮政编码
  • 经过哈希处理的名字
  • 经过哈希处理的姓氏

如果缺少任何这些字段,则无法匹配地址。

虽然客户名单只能包含一个 upload_key_type,但您可以为 CONTACT_INFO 的 upload_key_type 上传多种类型的联系信息。 建议您这样做,以提高匹配率。

增强型转化的电子邮件地址格式

为了确保增强型转化的匹配率最高,您必须按照以下步骤正确设置电子邮件地址的格式并进行哈希处理:

  1. 应用标准标准化(所有电子邮件地址):

    • 移除开头和结尾处的所有 ASCII 空格。
    • 将电子邮件地址中的所有字符转换为小写。
  2. 应用特定于网域的标准化(仅限 gmail.com 和 googlemail.com):

    • 将电子邮件地址转换为小写后,检查网域(@ 符号之后的部分)是否为 gmail.com 或 googlemail.com。
    • 移除句点 (.): 从用户名部分(@ 符号之前的部分)中移除所有句点字符。
    • 移除加号后缀 (+): 从用户名部分中移除第一个加号 (+) 以及后续所有字符(直到 @ 符号)。
      • 示例:Jane.Doe+Shopping@googlemail.com 首先变为 jane.doe+shopping@googlemail.com(小写),然后变为 janedoe@googlemail.com(Gmail 和 Googlemail 规则)。
    • 其他网域: 对于 gmail.com 或 googlemail.com 以外的任何网域,请勿从 用户名中移除句点或加号后缀。仅应用标准标准化(移除空格并转换为小写)。
      • 示例:user.name+NYC@Example.com 标准化为 user.name+nyc@example.com。
  3. 对标准化后的电子邮件地址进行哈希处理:

    • 将标准化后的电子邮件字符串编码为 UTF-8 字节。
    • 使用 SHA-256 算法对 UTF-8 字节进行哈希处理。
    • 将生成的摘要格式设置为 小写十六进制字符串 (64 个字符)。
import hashlib
import re


def normalize_and_hash_email(email: str) -> str:
    """Normalizes and SHA-256 hashes an email address for enhanced conversions."""
    normalized = email.strip().lower()
    parts = normalized.split("@")
    if len(parts) != 2:
        raise ValueError("Invalid email address format.")
    username, domain = parts
    if domain in ("gmail.com", "googlemail.com"):
        username = username.replace(".", "")
        username = re.sub(r"\+.*$", "", username)
    normalized = f"{username}@{domain}"
    return hashlib.sha256(normalized.encode("utf-8")).hexdigest()

上传 CRM ID

如需使用 CRM ID 填充客户名单,请将 upload_key_type 设置为 CRM_ID。CRM ID 与广告客户生成和分配的用户 ID 匹配。这类似于上传 MOBILE_ADVERTISING_ID 实例,但您需要改为填充 UserIdentifier 对象的 third_party_user_id 字段。

上传移动设备 ID

与使用电子邮件进行目标客户匹配类似,您可以使用广告标识符 (IDFA) 或 Google 广告 ID (AAID) 移动设备 ID 执行目标客户匹配。为此,请先指定 app_id 属性并将 upload_key_type 设置为 MOBILE_ADVERTISING_ID,然后再使用用户名单通过移动设备 ID 进行目标客户匹配 。

代码示例

以下示例使用 OfflineUserDataJobOperation 将客户联系信息附加到客户名单。

Java

// Creates a raw input list of unhashed user information, where each element of the list
// represents a single user and is a map containing a separate entry for the keys "email",
// "phone", "firstName", "lastName", "countryCode", and "postalCode". In your application, this
// data might come from a file or a database.
List<Map<String, String>> rawRecords = new ArrayList<>();
// The first user data has an email address and a phone number.
Map<String, String> rawRecord1 =
    ImmutableMap.<String, String>builder()
        .put("email", "dana@example.com")
        // Phone number to be converted to E.164 format, with a leading '+' as required. This
        // includes whitespace, dashes, and parentheses that will be removed later.
        .put("phone", "+1 (800) 555-0101")
        .build();
// The second user data has an email address, a mailing address, and a phone number.
Map<String, String> rawRecord2 =
    ImmutableMap.<String, String>builder()
        // Email address that includes a period (.) and plus (+) suffix before the Gmail domain.
        .put("email", "alex.2+myalias@gmail.com")
        // Address that includes all four required elements: first name, last name, country
        // code, and postal code.
        .put("firstName", "Alex")
        .put("lastName", "Quinn")
        .put("countryCode", "US")
        .put("postalCode", "94045")
        // Phone number to be converted to E.164 format, with a leading '+' as required.
        .put("phone", "+1 800-555-0102")
        .build();
// The third user data only has an email address.
Map<String, String> rawRecord3 =
    ImmutableMap.<String, String>builder().put("email", "charlie@example.com").build();
// Adds the raw records to the raw input list.
rawRecords.add(rawRecord1);
rawRecords.add(rawRecord2);
rawRecords.add(rawRecord3);

// Iterates over the raw input list and creates a UserData object for each record.
List<UserData> userDataList = new ArrayList<>();
for (Map<String, String> rawRecord : rawRecords) {
  // Creates a builder for the UserData object that represents a member of the user list.
  UserData.Builder userDataBuilder = UserData.newBuilder();
  // 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
  // hashedEmail, hashedPhoneNumber, mobileId, thirdPartyUserId, or addressInfo. 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 hashedPhoneNumber.
  //
  // UserIdentifier incorrectlyPopulatedUserIdentifier =
  //     UserIdentifier.newBuilder()
  //         .setHashedEmail("...")
  //         .setHashedPhoneNumber("...")
  //         .build();
  //
  // The separate 'if' statements below demonstrate the correct approach for creating a UserData
  // for a member with multiple UserIdentifiers.

  // Checks if the record has an email address, and if so, adds a UserIdentifier for it.
  if (rawRecord.containsKey("email")) {
    UserIdentifier hashedEmailIdentifier =
        UserIdentifier.newBuilder()
            .setHashedEmail(normalizeAndHashEmailAddress(sha256Digest, rawRecord.get("email")))
            .build();
    // Adds the hashed email identifier to the UserData object's list.
    userDataBuilder.addUserIdentifiers(hashedEmailIdentifier);
  }

  // Checks if the record has a phone number, and if so, adds a UserIdentifier for it.
  if (rawRecord.containsKey("phone")) {
    UserIdentifier hashedPhoneNumberIdentifier =
        UserIdentifier.newBuilder()
            .setHashedPhoneNumber(
                normalizeAndHashPhoneNumber(sha256Digest, rawRecord.get("phone")))
            .build();
    // Adds the hashed phone number identifier to the UserData object's list.
    userDataBuilder.addUserIdentifiers(hashedPhoneNumberIdentifier);
  }

  // Checks if the record has all the required mailing address elements, and if so, adds a
  // UserIdentifier for the mailing address.
  if (rawRecord.containsKey("firstName")) {
    // Checks if the record contains all the other required elements of a mailing address.
    Set<String> missingAddressKeys = new HashSet<>();
    for (String addressKey : new String[] {"lastName", "countryCode", "postalCode"}) {
      if (!rawRecord.containsKey(addressKey)) {
        missingAddressKeys.add(addressKey);
      }
    }

    if (!missingAddressKeys.isEmpty()) {
      System.out.printf(
          "Skipping addition of mailing address information because the following required keys"
              + " are missing: %s%n",
          missingAddressKeys);
    } else {
      // Creates an OfflineUserAddressInfo object that contains all the required elements of a
      // mailing address.
      OfflineUserAddressInfo addressInfo =
          OfflineUserAddressInfo.newBuilder()
              .setHashedFirstName(
                  normalizeAndHash(sha256Digest, rawRecord.get("firstName"), false))
              .setHashedLastName(
                  normalizeAndHash(sha256Digest, rawRecord.get("lastName"), false))
              .setCountryCode(rawRecord.get("countryCode"))
              .setPostalCode(rawRecord.get("postalCode"))
              .build();
      UserIdentifier addressIdentifier =
          UserIdentifier.newBuilder().setAddressInfo(addressInfo).build();
      // Adds the address identifier to the UserData object's list.
      userDataBuilder.addUserIdentifiers(addressIdentifier);
    }
  }

  if (!userDataBuilder.getUserIdentifiersList().isEmpty()) {
    // Builds the UserData and adds it to the list.
    userDataList.add(userDataBuilder.build());
  }
}

// Creates the operations to add users.
List<OfflineUserDataJobOperation> operations = new ArrayList<>();
for (UserData userData : userDataList) {
  operations.add(OfflineUserDataJobOperation.newBuilder().setCreate(userData).build());
}
      

C#

// Creates a raw input list of unhashed user information, where each element of the list
// represents a single user and is a map containing a separate entry for the keys
// "email", "phone", "firstName", "lastName", "countryCode", and "postalCode".
// In your application, this data might come from a file or a database.
List<Dictionary<string, string>> rawRecords = new List<Dictionary<string, string>>();

// The first user data has an email address and a phone number.
Dictionary<string, string> rawRecord1 = new Dictionary<string, string>();
rawRecord1.Add("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.
rawRecord1.Add("phone", "+1 800 5550101");

// The second user data has an email address, a mailing address, and a phone number.
Dictionary<string, string> rawRecord2 = new Dictionary<string, string>();
// Email address that includes a period (.) before the Gmail domain.
rawRecord2.Add("email", "alex.2@example.com");
// Address that includes all four required elements: first name, last name, country
// code, and postal code.
rawRecord2.Add("firstName", "Alex");
rawRecord2.Add("lastName", "Quinn");
rawRecord2.Add("countryCode", "US");
rawRecord2.Add("postalCode", "94045");
// Phone number to be converted to E.164 format, with a leading '+' as required.
// This includes whitespace that will be removed later.
rawRecord2.Add("phone", "+1 800 5550102");

// The third user data only has an email address.
Dictionary<string, string> rawRecord3 = new Dictionary<string, string>();
rawRecord3.Add("email", "charlie@example.com");

// Adds the raw records to the raw input list.
rawRecords.Add(rawRecord1);
rawRecords.Add(rawRecord2);
rawRecords.Add(rawRecord3);

// Iterates over the raw input list and creates a UserData object for each record.
List<UserData> userDataList = new List<UserData>();
foreach (Dictionary<string, string> rawRecord in rawRecords) {
    // Creates a UserData object that represents a member of the user list.
    UserData userData = new 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 hashedEmail, hashedPhoneNumber, mobileId, thirdPartyUserId,
    // or addressInfo.
    // 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 hashedPhoneNumber.
    //
    // UserIdentifier incorrectlyPopulatedUserIdentifier = new UserIdentifier()
    // {
    //      HashedEmail = "...",
    //      HashedPhoneNumber = "..."
    // };
    //
    // The separate 'if' statements below demonstrate the correct approach for creating
    // a UserData for a member with multiple UserIdentifiers.

    // Checks if the record has an email address, and if so, adds a UserIdentifier
    // for it.
    if (rawRecord.ContainsKey("email")) {
        UserIdentifier hashedEmailIdentifier = new UserIdentifier()
        {
            HashedEmail = NormalizeAndHash(rawRecord["email"], true)
        };

        userData.UserIdentifiers.Add(hashedEmailIdentifier);
    }

    // Checks if the record has a phone number, and if so, adds a UserIdentifier for it.
    if (rawRecord.ContainsKey("phone")) {
        UserIdentifier hashedPhoneNumberIdentifier = new UserIdentifier()
        {
            HashedPhoneNumber = NormalizeAndHash(rawRecord["phone"], true)
        };

        // Adds the hashed phone number identifier to the UserData object's list.
        userData.UserIdentifiers.Add(hashedPhoneNumberIdentifier);
    }

    // Checks if the record has all the required mailing address elements, and if so,
    // adds a UserIdentifier for the mailing address.
    if (rawRecord.ContainsKey("firstName")) {
        // Checks if the record contains all the other required elements of a mailing
        // address.
        HashSet<string> missingAddressKeys = new HashSet<string>();
        foreach (string addressKey in new string[] {"lastName", "countryCode",
            "postalCode"}) {
        if (!rawRecord.ContainsKey(addressKey)) {
            missingAddressKeys.Add(addressKey);
        }
        }

        if (!missingAddressKeys.Any()) {
        Console.WriteLine(
            $"Skipping addition of mailing address information because the following " +
                "required keys are missing: {missingAddressKeys}");
        } else {
            // Creates an OfflineUserAddressInfo object that contains all the required
            // elements of a mailing address.
            OfflineUserAddressInfo addressInfo = new OfflineUserAddressInfo()
            {
                HashedFirstName = NormalizeAndHash(rawRecord["firstName"]),
                HashedLastName = NormalizeAndHash(rawRecord["lastName"]),
                CountryCode = rawRecord["countryCode"],
                PostalCode = rawRecord["postalCode"]
            };

            UserIdentifier addressIdentifier = new UserIdentifier()
            {
                AddressInfo = addressInfo
            };

            // Adds the address identifier to the UserData object's list.
            userData.UserIdentifiers.Add(addressIdentifier);
        }
    }

    if (userData.UserIdentifiers.Any())
    {
        userDataList.Add(userData);
    }
}

// Creates the operations to add the users.
List<OfflineUserDataJobOperation> operations = new List<OfflineUserDataJobOperation>();
foreach(UserData userData in userDataList)
{
    operations.Add(new OfflineUserDataJobOperation()
    {
        Create = userData
    });
}
      

PHP

// Creates a raw input list of unhashed user information, where each element of the list
// represents a single user and is a map containing a separate entry for the keys 'email',
// 'phone', 'firstName', 'lastName', 'countryCode', and 'postalCode'. In your application,
// this data might come from a file or a database.
$rawRecords = [];
// The first user data has an email address and a phone number.
$rawRecord1 = [
    // 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, dashes, and parentheses that will be removed later.
    'phone' => '+1 (800) 555-0101'
];
$rawRecords[] = $rawRecord1;

// The second user data has an email address, a mailing address, and a phone number.
$rawRecord2 = [
    // Email address that includes a period (.) and plus (+) suffix before the Gmail domain.
    'email' => 'alex.2+myalias@gmail.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-555-0102',
];
$rawRecords[] = $rawRecord2;

// The third user data only has an email address.
$rawRecord3 = ['email' => 'charlie@example.com'];
$rawRecords[] = $rawRecord3;

// Iterates over the raw input list and creates a UserData object for each record.
$userDataList = [];
foreach ($rawRecords as $rawRecord) {
    // 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'.
    //
    // $incorrectlyPopulatedUserIdentifier = new UserIdentifier();
    // $incorrectlyPopulatedUserIdentifier->setHashedEmail('...');
    // $incorrectlyPopulatedUserIdentifier->setHashedPhoneNumber('...');
    //
    // The separate 'if' statements below demonstrate the correct approach for creating a
    // UserData for a member with multiple UserIdentifiers.

    $userIdentifiers = [];
    // Checks if the record has an email address, and if so, adds a UserIdentifier for it.
    if (array_key_exists('email', $rawRecord)) {
        $hashedEmailIdentifier = new UserIdentifier(
            [
            'hashed_email' => self::normalizeAndHashEmailAddress($rawRecord['email'])
            ]
        );
        // Adds the hashed email identifier to the user identifiers list.
        $userIdentifiers[] = $hashedEmailIdentifier;
    }

    // Checks if the record has a phone number, and if so, adds a UserIdentifier for it.
    if (array_key_exists('phone', $rawRecord)) {
        $hashedPhoneNumberIdentifier = new UserIdentifier(
            [
            'hashed_phone_number' => self::normalizeAndHashPhoneNumber($rawRecord['phone'])
            ]
        );
        // Adds the hashed phone number identifier to the user identifiers list.
        $userIdentifiers[] = $hashedPhoneNumberIdentifier;
    }

    // Checks if the record has all the required mailing address elements, and if so, adds a
    // UserIdentifier for the mailing address.
    if (array_key_exists('firstName', $rawRecord)) {
        // Checks if the record contains all the other required elements of a mailing
        // address.
        $missingAddressKeys = [];
        foreach (['lastName', 'countryCode', 'postalCode'] as $addressKey) {
            if (!array_key_exists($addressKey, $rawRecord)) {
                $missingAddressKeys[] = $addressKey;
            }
        }
        if (!empty($missingAddressKeys)) {
            printf(
                "Skipping addition of mailing address information because the "
                . "following required keys are missing: %s%s",
                json_encode($missingAddressKeys),
                PHP_EOL
            );
        } else {
            // Creates an OfflineUserAddressInfo object that contains all the required
            // elements of a mailing address.
            $addressIdentifier = new UserIdentifier(
                [
                'address_info' => new OfflineUserAddressInfo(
                    [
                    'hashed_first_name' => self::normalizeAndHash(
                        $rawRecord['firstName'],
                        false
                    ),
                    'hashed_last_name' => self::normalizeAndHash(
                        $rawRecord['lastName'],
                        false
                    ),
                    'country_code' => $rawRecord['countryCode'],
                    'postal_code' => $rawRecord['postalCode']
                    ]
                )
                ]
            );
            // Adds the address identifier to the user identifiers list.
            $userIdentifiers[] = $addressIdentifier;
        }
    }
    if (!empty($userIdentifiers)) {
        // Builds the UserData and adds it to the list.
        $userDataList[] = new UserData(['user_identifiers' => $userIdentifiers]);
    }
}

// Creates the operations to add users.
$operations = array_map(
    function (UserData $userData) {
        return new OfflineUserDataJobOperation(['create' => $userData]);
    },
    $userDataList
);
      

Python

def build_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 a
    separate entry for the keys "email", "phone", "first_name", "last_name",
    "country_code", and "postal_code". In your application, this data might come
    from 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.
    for record in raw_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" in record:
            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" in record:
            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" in record:
            required_keys = ("last_name", "country_code", "postal_code")
            # Checks if the record contains all the other required elements of
            # a mailing address.
            if not all(key in record for key in required_keys):
                # Determines which required elements are missing from the
                # record.
                missing_keys = record.keys() - required_keys
                print(
                    "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_info
                address_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.
        if user_data.user_identifiers:
            operation: OfflineUserDataJobOperation = client.get_type(
                "OfflineUserDataJobOperation"
            )
            operation.create = user_data
            operations.append(operation)
      

Ruby

# 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.map do |record|
  client.resource.user_data do |data|
    if record[:email]
      data.user_identifiers << client.resource.user_identifier do |ui|
        ui.hashed_email = normalize_and_hash(record[:email], true)
      end
    end
    if record[:phone]
      data.user_identifiers << client.resource.user_identifier do |ui|
        ui.hashed_phone_number = normalize_and_hash(record[:phone], true)
      end
    end
    if record[:first_name]
      # Check that we have all the required information.
      missing_keys = [:last_name, :country_code, :postal_code].reject {|key|
        record[key].nil?
      }
      if missing_keys.empty?
        # If nothing is missing, add the address.
        data.user_identifiers << client.resource.user_identifier do |ui|
          ui.address_identifier = client.resource.offline_user_address_info do |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]
          end
        end
      else
        # If some data is missing, skip this entry.
        puts "Skipping addition of mailing information because the following keys are missing:" \
          "#{missing_keys}"
      end
    end
  end
end

operations = user_data_list.map do |user_data|
  client.operation.create_resource.offline_user_data_job(user_data)
end
      

Perl

  # 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 = [];
  foreach my $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::V25::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::V25::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::V25::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  = [];

      foreach my $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::V25::Common::UserIdentifier->new({
              addressInfo =>
                Google::Ads::GoogleAds::V25::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::V25::Common::UserData->new({
          userIdentifiers => [$user_identifiers]});
      push(
        @$operations,
        Google::Ads::GoogleAds::V25::Services::OfflineUserDataJobService::OfflineUserDataJobOperation
          ->new({
            create => $user_data
          }));
    }
  }
      

curl

第 3 步:验证名单上传和匹配率

当 OfflineUserDataJob 处于 SUCCESS 状态 时,估计的匹配 率会显示在 operation_metadata.match_rate_range 字段中。如果您在作业完成之前查询此字段,则此字段中的值可能为 0。为了确保匹配率已准备好进行验证,并且名单已准备好进行定位,我们建议您轮询作业以确定其是否已完成。作业可能需要 10 分钟到 24 小时才能完成。

用于检查作业状态的代码示例

Java

private void checkJobStatus(
    GoogleAdsClient googleAdsClient, long customerId, String offlineUserDataJobResourceName) {
  try (GoogleAdsServiceClient googleAdsServiceClient =
      googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {
    String query =
        String.format(
            "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 = '%s'",
            offlineUserDataJobResourceName);
    // Issues the query and gets the GoogleAdsRow containing the job from the response.
    GoogleAdsRow googleAdsRow =
        googleAdsServiceClient
            .search(Long.toString(customerId), query)
            .iterateAll()
            .iterator()
            .next();
    OfflineUserDataJob offlineUserDataJob = googleAdsRow.getOfflineUserDataJob();
    System.out.printf(
        "Offline user data job ID %d with type '%s' has status: %s%n",
        offlineUserDataJob.getId(), offlineUserDataJob.getType(), offlineUserDataJob.getStatus());
    OfflineUserDataJobStatus jobStatus = offlineUserDataJob.getStatus();
    if (OfflineUserDataJobStatus.SUCCESS == jobStatus) {
      // Prints information about the user list.
      printCustomerMatchUserListInfo(
          googleAdsClient,
          customerId,
          offlineUserDataJob.getCustomerMatchUserListMetadata().getUserList());
    } else if (OfflineUserDataJobStatus.FAILED == jobStatus) {
      System.out.printf("  Failure reason: %s%n", offlineUserDataJob.getFailureReason());
    } else if (OfflineUserDataJobStatus.PENDING == jobStatus
        || OfflineUserDataJobStatus.RUNNING == jobStatus) {
      System.out.println();
      System.out.printf(
          "To check the status of the job periodically, use the following GAQL query with"
              + " GoogleAdsService.search:%n%s%n",
          query);
    }
  }
}

      

C#

private static void CheckJobStatusAndPrintResults(GoogleAdsClient client, long customerId,
    string offlineUserDataJobResourceName)
{
    // Get the GoogleAdsService.
    GoogleAdsServiceClient service = client.GetService(Services.V25.GoogleAdsService);

    string 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 = '{offlineUserDataJobResourceName}'";

    // Issues the query and gets the GoogleAdsRow containing the job from the response.
    GoogleAdsRow googleAdsRow = service.Search(customerId.ToString(), query).First();

    OfflineUserDataJob offlineUserDataJob = googleAdsRow.OfflineUserDataJob;
    Console.WriteLine($"Offline user data job ID {offlineUserDataJob.Id} with type " +
        $"'{offlineUserDataJob.Type}' has status: {offlineUserDataJob.Status}");

    switch (offlineUserDataJob.Status)
    {
        case OfflineUserDataJobStatus.Success:
            // Prints information about the user list.
            PrintCustomerMatchUserListInfo(client, customerId,
                offlineUserDataJob.CustomerMatchUserListMetadata.UserList);
            break;

        case OfflineUserDataJobStatus.Failed:
            Console.WriteLine($"  Failure reason: {offlineUserDataJob.FailureReason}");
            break;

        case OfflineUserDataJobStatus.Pending:
        case OfflineUserDataJobStatus.Running:
            Console.WriteLine("To check the status of the job periodically, use the " +
                $"following GAQL query with GoogleAdsService.search:\n\n{query}");
            break;
    }
}
      

PHP

    private static function checkJobStatus(
        GoogleAdsClient $googleAdsClient,
        int $customerId,
        string $offlineUserDataJobResourceName
    ) {
        $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();

        // Creates a query that retrieves the offline user data job.
        $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 = '$offlineUserDataJobResourceName'";

        // Issues a search request to get the GoogleAdsRow containing the job from the response.
        /**
 * @var GoogleAdsRow $googleAdsRow
*/
        $googleAdsRow =
            $googleAdsServiceClient->search(SearchGoogleAdsRequest::build($customerId, $query))
            ->getIterator()
            ->current();
        $offlineUserDataJob = $googleAdsRow->getOfflineUserDataJob();

        // Prints out some information about the offline user data job.
        $offlineUserDataJobStatus = $offlineUserDataJob->getStatus();
        printf(
            "Offline user data job ID %d with type '%s' has status: %s.%s",
            $offlineUserDataJob->getId(),
            OfflineUserDataJobType::name($offlineUserDataJob->getType()),
            OfflineUserDataJobStatus::name($offlineUserDataJobStatus),
            PHP_EOL
        );

        if ($offlineUserDataJobStatus === OfflineUserDataJobStatus::SUCCESS) {
            // Prints information about the user list.
            self::printCustomerMatchUserListInfo(
                $googleAdsClient,
                $customerId,
                $offlineUserDataJob->getCustomerMatchUserListMetadata()->getUserList()
            );
        } elseif ($offlineUserDataJobStatus === OfflineUserDataJobStatus::FAILED) {
            printf("  Failure reason: %s.%s", $offlineUserDataJob->getFailureReason(), PHP_EOL);
        } elseif (
            $offlineUserDataJobStatus === OfflineUserDataJobStatus::PENDING
            || $offlineUserDataJobStatus === OfflineUserDataJobStatus::RUNNING
        ) {
            printf(
                '%1$sTo check the status of the job periodically, use the following GAQL query with'
                . ' GoogleAdsService.search:%1$s%2$s%1$s',
                PHP_EOL,
                $query
            );
        }
    }
      

Python

def check_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 is
    printed. Otherwise, a GAQL query will be printed, which can be used to
    check the job status at a later date.

    Offline user data jobs may take 6 hours or more to complete, so checking the
    status 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 offline
            user data job to get the status of.
    """
    query: str = f"""
        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"""

    # 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_job
    status_name: str = offline_user_data_job_result.status.name
    user_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}"
    )

    if status_name == "SUCCESS":
        print_customer_match_user_list_info(
            client, customer_id, user_list_resource_name
        )
    elif status_name == "FAILED":
        print(
            f"\tFailure Reason: {offline_user_data_job_result.failure_reason}"
        )
    elif status_name in ("PENDING", "RUNNING"):
        print(
            "To check the status of the job periodically, use the following "
            f"GAQL query with GoogleAdsService.Search: {query}"
        )
      

Ruby

def check_job_status(client, customer_id, offline_user_data_job)
  query = <<~QUERY
    SELECT
      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}'
  QUERY

  row = client.service.google_ads.search(
    customer_id: customer_id,
    query: query,
  ).first

  job = row.offline_user_data_job
  puts "Offline user data job ID #{job.id} with type '#{job.type}' has status: #{job.status}."

  case job.status
  when :SUCCESS
    print_customer_match_user_list(client, customer_id, job.customer_match_user_list_metadata.user_list)
  when :FAILED
    puts "  Failure reason: #{job.failure_reason}"
  else
    puts "  To check the status of the job periodically, use the following GAQL " \
      "query with GoogleAdsService.search:"
    puts query
  end
end
      

Perl

sub check_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::V25::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 ($status eq SUCCESS) {
    print_customer_match_user_list_info($api_client, $customer_id,
      $offline_user_data_job->{customerMatchUserListMetadata}{userList});
  } elsif ($status eq FAILED) {
    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";
  }

  return 1;
}
      

curl

如需验证名单规模,您可以查询 user_list 资源。

用于查询 user_list 资源的代码示例

Java

try (GoogleAdsServiceClient googleAdsServiceClient =
    googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {
  // Creates a query that retrieves the user list.
  String query =
      String.format(
          "SELECT user_list.size_for_display, user_list.size_for_search "
              + "FROM user_list "
              + "WHERE user_list.resource_name = '%s'",
          userListResourceName);

  // Constructs the SearchGoogleAdsStreamRequest.
  SearchGoogleAdsStreamRequest request =
      SearchGoogleAdsStreamRequest.newBuilder()
          .setCustomerId(Long.toString(customerId))
          .setQuery(query)
          .build();

  // Issues the search stream request.
  ServerStream<SearchGoogleAdsStreamResponse> stream =
      googleAdsServiceClient.searchStreamCallable().call(request);
      

C#

 // Get the GoogleAdsService.
 GoogleAdsServiceClient service =
     client.GetService(Services.V25.GoogleAdsService);

 // Creates a query that retrieves the user list.
 string query =
     "SELECT user_list.size_for_display, user_list.size_for_search " +
     "FROM user_list " +
     $"WHERE user_list.resource_name = '{userListResourceName}'";
 // Issues a search stream request.
 service.SearchStream(customerId.ToString(), query,
    delegate (SearchGoogleAdsStreamResponse resp)
    {
        // Display the results.
        foreach (GoogleAdsRow userListRow in resp.Results)
        {
            UserList userList = userListRow.UserList;
            Console.WriteLine("The estimated number of users that the user list " +
                $"'{userList.ResourceName}' has is {userList.SizeForDisplay}" +
                $" for Display and {userList.SizeForSearch} for Search.");
        }
    }
);
      

PHP

        $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();

        // Creates a query that retrieves the user list.
        $query =
            "SELECT user_list.size_for_display, user_list.size_for_search " .
            "FROM user_list " .
            "WHERE user_list.resource_name = '$userListResourceName'";

        // Issues a search stream request.
        /**
 * @var GoogleAdsServerStreamDecorator $stream
*/
        $stream = $googleAdsServiceClient->searchStream(
            SearchGoogleAdsStreamRequest::build($customerId, $query)
        );
      

Python

googleads_service_client: GoogleAdsServiceClient = client.get_service(
    "GoogleAdsService"
)

# Creates a query that retrieves the user list.
query: str = f"""
    SELECT
      user_list.size_for_display,
      user_list.size_for_search
    FROM user_list
    WHERE 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)
)
      

Ruby

query = <<~EOQUERY
  SELECT user_list.size_for_display, user_list.size_for_search
  FROM user_list
  WHERE user_list.resource_name = #{user_list}
EOQUERY

response = client.service.google_ads.search_stream(
  customer_id: customer_id,
  query: query,
)
      

Perl

# 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::V25::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
  });
      

curl

出于隐私保护方面的考虑,在名单至少包含 100 名成员之前,用户名单规模会显示为零。之后,规模会四舍五入到两个最重要的数字。

您可以使用 Google Ads 查询语言通过 offline_user_data_job资源提取执行OfflineUserDataJob期间出现的错误。不过,此报告不包含任何匹配失败的信息,因为执行匹配时只会比较哈希值。如果您遇到客户名单方面的问题,请参阅问题排查指南。

与 Google Ads 界面进行比较

从 Google Ads 界面的受众群体管理器中查看名单时,名单可能会比预期的小。此视图显示的是名单中的活跃用户数。 如需了解详情,请参阅此问题排查指南。

由于名单最多可能需要 24 小时才能填充成员,因此您 如果向受众群体名单上传数据的频率超过每 12 小时一次,则可能会在 Google Ads 界面中看到In Progress状态。

第 4 步:定位到您的名单

您可以在广告组一级或广告系列一级定位到您的名单。此过程与 API 中的其他类型的定位条件类似。

用于将广告组中的定位广告定位到用户名单的代码示例

Java

private String targetAdsInAdGroupToUserList(
    GoogleAdsClient googleAdsClient, long customerId, long adGroupId, String userList) {
  // Creates the ad group criterion targeting members of the user list.
  AdGroupCriterion adGroupCriterion =
      AdGroupCriterion.newBuilder()
          .setAdGroup(ResourceNames.adGroup(customerId, adGroupId))
          .setUserList(UserListInfo.newBuilder().setUserList(userList).build())
          .build();

  // Creates the operation.
  AdGroupCriterionOperation operation =
      AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterion).build();

  // Creates the ad group criterion service.
  try (AdGroupCriterionServiceClient adGroupCriterionServiceClient =
      googleAdsClient.getLatestVersion().createAdGroupCriterionServiceClient()) {
    // Adds the ad group criterion.
    MutateAdGroupCriteriaResponse response =
        adGroupCriterionServiceClient.mutateAdGroupCriteria(
            Long.toString(customerId), ImmutableList.of(operation));
    // Gets and prints the results.
    String adGroupCriterionResourceName = response.getResults(0).getResourceName();
    System.out.printf(
        "Successfully created ad group criterion with resource name '%s' "
            + "targeting user list with resource name '%s' with ad group with ID %d.%n",
        adGroupCriterionResourceName, userList, adGroupId);
    return adGroupCriterionResourceName;
  }
}

      

C#

private string TargetAdsInAdGroupToUserList(
    GoogleAdsClient client, long customerId, long adGroupId, string userListResourceName)
{
    // Get the AdGroupCriterionService client.
    AdGroupCriterionServiceClient adGroupCriterionServiceClient = client.GetService
        (Services.V25.AdGroupCriterionService);

    // Create the ad group criterion targeting members of the user list.
    AdGroupCriterion adGroupCriterion = new AdGroupCriterion
    {
        AdGroup = ResourceNames.AdGroup(customerId, adGroupId),
        UserList = new UserListInfo
        {
            UserList = userListResourceName
        }
    };

    // Create the operation.
    AdGroupCriterionOperation adGroupCriterionOperation = new AdGroupCriterionOperation
    {
        Create = adGroupCriterion
    };

    // Add the ad group criterion, then print and return the new criterion's resource name.
    MutateAdGroupCriteriaResponse mutateAdGroupCriteriaResponse =
        adGroupCriterionServiceClient.MutateAdGroupCriteria(customerId.ToString(),
            new[] { adGroupCriterionOperation });

    string adGroupCriterionResourceName =
        mutateAdGroupCriteriaResponse.Results.First().ResourceName;
    Console.WriteLine("Successfully created ad group criterion with resource name " +
        $"'{adGroupCriterionResourceName}' targeting user list with resource name " +
        $"'{userListResourceName}' with ad group with ID {adGroupId}.");
    return adGroupCriterionResourceName;
}
      

PHP

    private static function targetAdsInAdGroupToUserList(
        GoogleAdsClient $googleAdsClient,
        int $customerId,
        int $adGroupId,
        string $userListResourceName
    ): string {
        // Creates the ad group criterion targeting members of the user list.
        $adGroupCriterion = new AdGroupCriterion(
            [
            'ad_group' => ResourceNames::forAdGroup($customerId, $adGroupId),
            'user_list' => new UserListInfo(['user_list' => $userListResourceName])
            ]
        );

        // Creates the operation.
        $operation = new AdGroupCriterionOperation();
        $operation->setCreate($adGroupCriterion);

        // Issues a mutate request to add an ad group criterion.
        $adGroupCriterionServiceClient = $googleAdsClient->getAdGroupCriterionServiceClient();
        /**
 * @var MutateAdGroupCriteriaResponse $adGroupCriterionResponse
*/
        $adGroupCriterionResponse = $adGroupCriterionServiceClient->mutateAdGroupCriteria(
            MutateAdGroupCriteriaRequest::build($customerId, [$operation])
        );

        $adGroupCriterionResourceName =
            $adGroupCriterionResponse->getResults()[0]->getResourceName();
        printf(
            "Successfully created ad group criterion with resource name '%s' " .
            "targeting user list with resource name '%s' with ad group with ID %d.%s",
            $adGroupCriterionResourceName,
            $userListResourceName,
            $adGroupId,
            PHP_EOL
        );

        return $adGroupCriterionResourceName;
    }
      

Python

def target_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 group
            criterion.
        ad_group_id: a str ID for an ad group used to create an ad group
            criterion 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.create
    ad_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_name

    ad_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_name
    print(
        "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}."
    )
    return resource_name
      

Ruby

def target_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_criterion do |agc|
    agc.ad_group = client.path.ad_group(customer_id, ad_group_id)
    agc.user_list = client.resource.user_list_info do |info|
      info.user_list = user_list
    end
  end

  # 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_name
  puts "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_name
end
      

Perl

sub target_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::V25::Resources::AdGroupCriterion->new({
      adGroup => Google::Ads::GoogleAds::V25::Utils::ResourceNames::ad_group(
        $customer_id, $ad_group_id
      ),
      userList => Google::Ads::GoogleAds::V25::Common::UserListInfo->new({
          userList => $user_list_resource_name
        })});

  # Create the operation.
  my $ad_group_criterion_operation =
    Google::Ads::GoogleAds::V25::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;
}
      

curl

定位到多个客户名单

crm_based_user_list 只能与另一个 crm_based_user_list 组合,使用 logical_user_list时。适用于 crm_based_user_list 的所有政策都适用于生成的用户名单。

管理和更新您的名单

创建客户名单后,您可能需要定期更新这些名单,以反映客户群的变化。借助 Google Ads API,您可以通过添加或移除成员来管理现有名单。

更新名单的主要操作包括:

  • 附加新用户:向现有名单添加新客户数据。
  • 移除特定用户:根据标识符从名单中移除个别用户。
  • 替换所有成员:清除现有名单并完全替换其内容,不过建议改为附加和移除。

请参阅管理客户名单。