This guide provides detailed instructions for uploading offline conversions
using the Campaign Manager 360 API Conversions
service. Before
continuing, it is recommended that you review the
Overview for an introduction to offline conversions and
to familiarize yourself with concepts discussed in this guide.
Configure conversion resources
The first step in the conversion upload process is creating one or more
Conversion
resource objects. Each of these objects
represent a single conversion event and contain a few required fields:
Field | Description |
---|---|
floodlightActivityId |
The Floodlight activity with which this conversion will be associated. |
floodlightConfigurationId |
The Floodlight configuration used by the specified activity. |
ordinal |
A value used to control how conversions from the same device or user on the same day are deduplicated. See the FAQ for more information. |
timestampMicros |
The timestamp of the conversion, in microseconds since the Unix epoch. |
Additionally, each object must contain one of the following fields:
Field | Description |
---|---|
encryptedUserId |
A single encrypted user ID obtained from the %m match macro or Data Transfer. |
encryptedUserIdCandidates[] |
A list of encrypted user IDs obtained from the %m match macro or Data Transfer. The first of these IDs with a recorded Floodlight exposure prior to the specified timestampMicros will be used. If none of the IDs match an existing exposure, an error will be thrown. |
dclid |
A Display Click Identifier generated by Campaign Manager 360 or Display & Video 360. |
gclid |
A Google Click Identifier generated by Google Ads or Search Ads 360. |
matchId |
A unique advertiser created identifier passed to Campaign Manager 360 via a Floodlight tag. |
mobileDeviceId |
An unencrypted mobile ID in the IDFA or AdID format or a Connected TV Identifier for Advertising (IFA) from a supported CTV device platform (Roku, Fire TV, Android TV, Apple TV, Xbox, Samsung, Vizio). Note that Google does not support YouTube Connected TV IFAs. |
Optional fields are documented in the reference documentation. Note that the quantity field, though optional, must be at least 1 for the conversion to count towards certain metrics (such as Total Conversions) when running reports.
The example below illustrates the creation of a simple conversion resource object:
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 )
Specify encryption info
If you plan to attribute offline conversions to encrypted user IDs, as in the previous example, you'll need to provide some details about how they're encrypted as part of your insert request. In particular, you'll need to know:
The encryption source, which describes where a batch of encrypted IDs came from. Acceptable values are
AD_SERVING
for IDs sourced from the %m match macro, orDATA_TRANSFER
for IDs sourced from Data Transfer files.The encryption entity, which is a unique set of values used to encrypt user IDs. These values normally relate to a Campaign Manager 360 account when the source is Data Transfer, or a Campaign Manager 360 advertiser when the source is the %m macro, but this is not always the case. If you're not sure, contact your Campaign Manager 360 account representative or Campaign Manager 360 support for more information.
When necessary, creating an
EncryptionInfo
object that specifies
these values is the second step in the conversion upload process:
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] )
Be aware that each insert request may contain only one EncryptionInfo
object. This means that all of the conversions included in a given request must
originate from the same source and use the same encryption entity.
Generate an insert request
The final step in this process is to upload your conversions with a call to
batchinsert
. This method accepts a
ConversionsBatchInsertRequest
object, which
combines the set of conversions to be uploaded with their associated encryption
info (when necessary):
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)
Be aware that Campaign Manager 360 attempts to insert each conversion in your
request on a best-effort basis, rather than inserting the entire batch as an
all-or-nothing transaction. If some conversions in a batch fail to insert,
others might still be inserted successfully. Therefore, it's recommended that
you inspect the returned
ConversionsBatchInsertResponse
, to
determine the status of each conversion:
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
The status
field of the response, as seen above, will contain a
ConversionStatus
object for every conversion
included in the original request. If you're only interested in conversions that
failed to insert, the hasFailures
field can be used to quickly determine if
any conversion in the provided batch failed.
Verify conversions were processed
Uploaded conversions will generally be processed and available to report on within 24 hours. To verify whether or not conversions you've uploaded were processed, it's recommended to run a Floodlight Impressions report. Unlike other attribution reports, these will return both attributed (associated with an ad) and unattributed conversions by default. This makes it ideal for quickly checking to see whether the conversions you've sent have made it to Campaign Manager 360.