Upload conversions

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 oneof 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.

Finally, each conversion has two metrics:

Field Description
quantity Required. The number of items associated with the conversion. Must be at least 1 for the conversion to count towards certain metrics (such as Total Conversions).
value The dollar amount associated with the conversion. Interpreted in the floodlight config owning advertiser currency.

Optional fields are documented in the reference documentation .

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:

  1. 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, or DATA_TRANSFER for IDs sourced from Data Transfer files.

  2. 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 anyconversion 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.

Create a Mobile Website
View Site in Mobile | Classic
Share by: