上传转化数据

本指南将详细说明如何使用 Campaign Manager 360 API Conversions 服务上传线下转化数据。在继续阅读本文之前,您最好先查看概览,简要了解一下线下转化并让自己熟悉本指南中要讨论的几个概念。

配置转化资源

转化数据上传流程的第一步是创建一个或多个 Conversion 资源对象。这些对象中的每一个均表示一个转化事件,且包含一些必填字段:

字段 说明
floodlightActivityId 要与该转化相关联的 Floodlight 活动。
floodlightConfigurationId 所指定的活动使用的 Floodlight 配置。
ordinal 一个值,用于控制如何对来自同一天中的同一设备或用户的转化数据执行重复数据删除操作。如需了解详情,请参阅常见问题解答
timestampMicros 相应转化的时间戳(自 Unix 新纪元至今的时间,以微秒为单位)。

此外,每个对象必须包含以下字段中的一个

字段 说明
encryptedUserId %m 匹配宏数据传输获取的单个经过加密的用户 ID。
encryptedUserIdCandidates[] %m 匹配宏数据传输获取的一系列经过加密的用户 ID。在这些 ID 中,系统会使用第一个所含 Floodlight 曝光是在指定的 timestampMicros 之前记录的 ID。如果这些 ID 无一与现有曝光匹配,系统将会报错。
dclid 由 Campaign Manager 360 或 Display & Video 360 生成的展示广告点击标识符
gclid 由 Google Ads 或 Search Ads 360 生成的 Google 点击标识符
matchId 通过 Floodlight 代码传递到 Campaign Manager 360 的由广告客户创建的唯一标识符。
mobileDeviceId IDFA 或 AdID 格式的未加密移动设备 ID,或支持的 CTV 设备平台(Roku、Fire TV、Android TV、Apple TV、Xbox、三星、Vizio)所用的联网电视 IFA(广告标识符)。请注意,Google 不支持 YouTube 联网电视 IFA。

参考文档介绍了选填字段。 请注意,quantity 字段是选填字段,但其值必须至少为 1,这样系统才能在生成报表时将转化计入特定指标(例如“总转化次数”)。

以下示例展示了如何创建一个简单的 Conversion 资源对象:

C#

// Generate a timestamp in milliseconds since Unix epoch.
TimeSpan timeSpan = DateTime.UtcNow - new DateTime(1970, 1, 1);
long currentTimeInMilliseconds = (long) timeSpan.TotalMilliseconds;

// Find the Floodlight configuration ID based on the provided activity ID.
FloodlightActivity floodlightActivity =
    service.FloodlightActivities.Get(profileId, floodlightActivityId).Execute();
long floodlightConfigurationId = (long) floodlightActivity.FloodlightConfigurationId;

// Create the conversion.
Conversion conversion = new Conversion();
conversion.EncryptedUserId = conversionUserId;
conversion.FloodlightActivityId = floodlightActivityId;
conversion.FloodlightConfigurationId = floodlightConfigurationId;
conversion.Ordinal = currentTimeInMilliseconds.ToString();
conversion.TimestampMicros = currentTimeInMilliseconds * 1000;

Java

long currentTimeInMilliseconds = System.currentTimeMillis();

// Find Floodlight configuration ID based on the provided activity ID.
FloodlightActivity floodlightActivity = reporting.floodlightActivities()
    .get(profileId, floodlightActivityId).execute();
long floodlightConfigurationId = floodlightActivity.getFloodlightConfigurationId();

Conversion conversion = new Conversion();
conversion.setEncryptedUserId(encryptedUserId);
conversion.setFloodlightActivityId(floodlightActivityId);
conversion.setFloodlightConfigurationId(floodlightConfigurationId);
conversion.setOrdinal(String.valueOf(currentTimeInMilliseconds));
conversion.setTimestampMicros(currentTimeInMilliseconds * 1000);

PHP

$currentTimeInMicros = time() * 1000 * 1000;

// Find Floodlight configuration ID based on provided activity ID.
$activity = $this->service->floodlightActivities->get(
    $values['user_profile_id'],
    $values['floodlight_activity_id']
);
$floodlightConfigId = $activity->getFloodlightConfigurationId();

$conversion = new Google_Service_Dfareporting_Conversion();
$conversion->setEncryptedUserId($values['encrypted_user_id']);
$conversion->setFloodlightActivityId($values['floodlight_activity_id']);
$conversion->setFloodlightConfigurationId($floodlightConfigId);
$conversion->setOrdinal($currentTimeInMicros);
$conversion->setTimestampMicros($currentTimeInMicros);

Python

# Look up the Floodlight configuration ID based on activity ID.
floodlight_activity = service.floodlightActivities().get(
    profileId=profile_id, id=floodlight_activity_id).execute()
floodlight_config_id = floodlight_activity['floodlightConfigurationId']

current_time_in_micros = int(time.time() * 1000000)

# Construct the conversion.
conversion = {
    'encryptedUserId': encrypted_user_id,
    'floodlightActivityId': floodlight_activity_id,
    'floodlightConfigurationId': floodlight_config_id,
    'ordinal': current_time_in_micros,
    'timestampMicros': current_time_in_micros
}

Ruby

# Look up the Floodlight configuration ID based on activity ID.
floodlight_activity = service.get_floodlight_activity(profile_id,
  floodlight_activity_id)
floodlight_config_id = floodlight_activity.floodlight_configuration_id

current_time_in_micros = DateTime.now.strftime('%Q').to_i * 1000

# Construct the conversion.
conversion = DfareportingUtils::API_NAMESPACE::Conversion.new(
  encrypted_user_id: encrypted_user_id,
  floodlight_activity_id: floodlight_activity_id,
  floodlight_configuration_id: floodlight_config_id,
  ordinal: current_time_in_micros,
  timestamp_micros: current_time_in_micros
)

指定加密信息

如果您计划将线下转化归因于经过加密的用户 ID(如上面的示例所述),您便需要在自己的插入请求中提供一些与此类用户 ID 的加密方式相关的详细信息。具体而言,您需要了解以下内容:

  1. 加密来源:用于说明一批经过加密的 ID 的来源。可接受的值为:AD_SERVING(适用于源自 %m 匹配宏的 ID),或 DATA_TRANSFER(适用于源自数据传输文件的 ID)。

  2. 加密实体:用于加密用户 ID 的一组不重复的值。这些值通常会与 Campaign Manager 360 账号相关(如果来源是数据传输)或与 Campaign Manager 360 广告客户相关(如果来源是 %m 宏),但也存在例外情况。如果不确定,请与您的 Campaign Manager 360 客户代表或 Campaign Manager 360 支持团队联系以了解详情。

必要时,请创建一个用于指定这些值的 EncryptionInfo 对象,这是转化数据上传流程的第二步:

C#

// Create the encryption info.
EncryptionInfo encryptionInfo = new EncryptionInfo();
encryptionInfo.EncryptionEntityId = encryptionEntityId;
encryptionInfo.EncryptionEntityType = encryptionEntityType;
encryptionInfo.EncryptionSource = encryptionSource;

Java

// Create the encryption info.
EncryptionInfo encryptionInfo = new EncryptionInfo();
encryptionInfo.setEncryptionEntityId(encryptionEntityId);
encryptionInfo.setEncryptionEntityType(encryptionEntityType);
encryptionInfo.setEncryptionSource(encryptionSource);

PHP

$encryptionInfo = new Google_Service_Dfareporting_EncryptionInfo();
$encryptionInfo->setEncryptionEntityId($values['encryption_entity_id']);
$encryptionInfo->setEncryptionEntityType($values['encryption_entity_type']);
$encryptionInfo->setEncryptionSource($values['encryption_source']);

Python

# Construct the encryption info.
encryption_info = {
    'encryptionEntityId': encryption_entity_id,
    'encryptionEntityType': encryption_entity_type,
    'encryptionSource': encryption_source
}

Ruby

# Construct the encryption info.
encryption_info = DfareportingUtils::API_NAMESPACE::EncryptionInfo.new(
  encryption_entity_id: encryption[:entity_id],
  encryption_entity_type: encryption[:entity_type],
  encryption_source: encryption[:source]
)

请注意,每项插入请求只能包含 1 个 EncryptionInfo 对象。这意味着,同一指定请求中包含的所有转化都必须源自同一来源,且使用同一加密实体。

生成插入请求

该流程的最后一步是通过调用 batchinsert 上传您的转化数据。此方法会接受一个 ConversionsBatchInsertRequest 对象,而该对象会将那组要上传的转化数据与相关加密信息整合到一起(如有必要):

C#

// Insert the conversion.
ConversionsBatchInsertRequest request = new ConversionsBatchInsertRequest();
request.Conversions = new List<Conversion>() { conversion };
request.EncryptionInfo = encryptionInfo;

ConversionsBatchInsertResponse response =
    service.Conversions.Batchinsert(request, profileId).Execute();

Java

ConversionsBatchInsertRequest request = new ConversionsBatchInsertRequest();
request.setConversions(ImmutableList.of(conversion));
request.setEncryptionInfo(encryptionInfo);

ConversionsBatchInsertResponse response = reporting.conversions()
    .batchinsert(profileId, request).execute();

PHP

$batch = new Google_Service_Dfareporting_ConversionsBatchInsertRequest();
$batch->setConversions([$conversion]);
$batch->setEncryptionInfo($encryptionInfo);

$result = $this->service->conversions->batchinsert(
    $values['user_profile_id'],
    $batch
);

Python

# Insert the conversion.
request_body = {
    'conversions': [conversion],
    'encryptionInfo': encryption_info
}
request = service.conversions().batchinsert(profileId=profile_id,
                                            body=request_body)
response = request.execute()

Ruby

# Construct the batch insert request.
batch_insert_request =
  DfareportingUtils::API_NAMESPACE::ConversionsBatchInsertRequest.new(
    conversions: [conversion],
    encryption_info: encryption_info
  )

# Insert the conversion.
result = service.batchinsert_conversion(profile_id, batch_insert_request)

请注意,Campaign Manager 360 会试着尽力将每一个转化事件都插入到您的请求中,而不是孤注一掷地成批插入。如果批量插入中有些转化事件插入失败,其他转化事件可能仍能成功插入。因此,建议您检查返回的 ConversionsBatchInsertResponse,以确定每个转化事件的状态:

C#

// Handle the batchinsert response.
if (!response.HasFailures.Value) {
  Console.WriteLine("Successfully inserted conversion for encrypted user ID {0}.",
      conversionUserId);
} else {
  Console.WriteLine("Error(s) inserting conversion for encrypted user ID {0}:",
      conversionUserId);

  ConversionStatus status = response.Status[0];
  foreach(ConversionError error in status.Errors) {
    Console.WriteLine("\t[{0}]: {1}", error.Code, error.Message);
  }
}

Java

if (!response.getHasFailures()) {
  System.out.printf("Successfully inserted conversion for encrypted user ID %s.%n",
      encryptedUserId);
} else {
  System.out.printf("Error(s) inserting conversion for encrypted user ID %s:%n",
      encryptedUserId);

  // Retrieve the conversion status and report any errors found. If multiple conversions
  // were included in the original request, the response would contain a status for each.
  ConversionStatus status = response.getStatus().get(0);
  for (ConversionError error : status.getErrors()) {
    System.out.printf("\t[%s]: %s.%n", error.getCode(), error.getMessage());
  }
}

PHP

if (!$result->getHasFailures()) {
    printf(
        'Successfully inserted conversion for encrypted user ID %s.',
        $values['encrypted_user_id']
    );
} else {
    printf(
        'Error(s) inserting conversion for encrypted user ID %s:<br><br>',
        $values['encrypted_user_id']
    );

    $status = $result->getStatus()[0];
    foreach ($status->getErrors() as $error) {
        printf('[%s] %s<br>', $error->getCode(), $error->getMessage());
    }
}

Python

if not response['hasFailures']:
  print ('Successfully inserted conversion for encrypted user ID %s.'
         % encrypted_user_id)
else:
  print ('Error(s) inserting conversion for encrypted user ID %s.'
         % encrypted_user_id)

  status = response['status'][0]
  for error in status['errors']:
    print '\t[%s]: %s' % (error['code'], error['message'])

Ruby

if result.has_failures
  puts format('Error(s) inserting conversion for encrypted user ID %s.',
    encrypted_user_id)

  status = result.status[0]
  status.errors.each do |error|
    puts format("\t[%s]: %s", error.code, error.message)
  end
else
  puts format('Successfully inserted conversion for encrypted user ID %s.',
    encrypted_user_id)
end

如上所示,对于原始请求中包含的每个转化事件,相应响应的 status 字段中都会有一个 ConversionStatus 对象与之对应。如果您只想了解未能成功插入的转化事件,可使用 hasFailures 字段来快速确定所提供的批量转化事件中是否有任何转化事件插入失败。

验证转化数据是否已得到处理

已上传的转化数据通常会在 24 小时内得到处理并可用于生成报表。如需验证您已上传的转化数据是否已得到处理,建议您生成 Floodlight 展示次数报告。与其他归因报表不同,此类报表默认同时返回已归因的转化(与某个广告相关联)和未归因的转化。因此,此类报表非常适合用于快速查看发送的转化数据是否已送达 Campaign Manager 360。