AI-generated Key Takeaways
-
The Google Ads API can be used to import offline call conversions to track valuable customer actions originating from phone calls generated by ads.
-
Importing call conversions requires enabling conversion tracking beforehand.
-
Specific fields like
consentmust be populated when creating aCallConversionobject, andpartial_failureshould always be set totruein the request. -
You need the caller ID, conversion date time, conversion action resource name, and optionally conversion value and currency to import a call conversion.
-
Requirements for importing a
CallConversioninclude the correctConversionActionType, an enabledConversionAction, conversion tracking being enabled at the time of the call, the correctcustomer_idin the request, and a non-negativeconversion_valuewith a properly formattedconversion_date_time.
You can use the Google Ads API to import offline call conversions into Google Ads in order to track when ads leads to phone calls and when those phone calls lead to valuable customer actions.
Mapping to the Google Ads UI this is the same as using the Uploadsconversion source followed by choosing Conversions from calls.
Create a call conversion
A few things to keep in mind when creating a CallConversion
:
-
You must populate the
consentfield of theCallConversionobject. -
The
partial_failureattribute of theUploadCallConversionsRequestshould always be set totrue. Follow the partial failures guidelines when handling valid and failed operations simultaneously. -
If you encounter a
TOO_RECENT_CONVERSION_ACTIONorTOO_RECENT_CALLresponse message to an import conversion request, wait 6 hours or 12 hours, respectively, before retrying the failed rows. -
It takes up to 3 hours for imported conversion statistics to appear in your Google Ads account.
- If a single request contains multiple operations for the same conversion,
a
DUPLICATE_CALL_CONVERSION_IN_REQUESTerror is returned.
- If a single request contains multiple operations for the same conversion,
a
-
If a user manually copies a Google forwarding number and calls it, no conversion can be recorded.
Import a call conversion
In order to associate your offline call conversions with a conversion action you
need the following information: the caller ID (phone number),
conversion date time, conversion action resource name and optionally the
conversion value and currency to the ConversionUploadService
.
For more details on the different inputs see our Help
Center
article. The code
example
also describes the formats for the different inputs.
Requirements
The following requirements must be met when importing a CallConversion
.
To avoid a ConversionUploadError.INVALID_CONVERSION_ACTION
error, the conversion_action
attribute
must refer to a ConversionAction
where:
-
The
ConversionActionTypeisUPLOAD_CALLS. -
The
statusof theConversionActionisENABLED. Otherwise, the operation fails with aNO_CONVERSION_ACTION_FOUNDerror. -
The
ConversionActionexists in the Google Ads conversion customer of the click's Google Ads account.
We recommend also setting the ConversionAction.category
to the
category that best describes your conversions.
In addition, the following conditions must be met:
-
At the time of the call , conversion tracking was enabled in the Google Ads conversion customer of the call's Google Ads account.
-
The
customer_idof theUploadCallConversionsRequestmust be the customer ID of the Google Ads conversion customer of the call's Google Ads account. Otherwise, the conversion import will result in aConversionUploadError.INVALID_CUSTOMER_FOR_CALLerror. -
The
conversion_valuemust be greater than or equal to zero. -
The
conversion_date_timemust have a timezone specified , and the format is asyyyy-mm-dd HH:mm:ss+|-HH:mm, for example:2022-01-01 19:32:45-05:00(ignoring daylight saving time) . The timezone can be for any valid value: it does not have to match the account's timezone. However, if you plan on comparing your imported conversion data with those in the Google Ads UI, we recommend using the same timezone as your Google Ads account so that the conversion counts match.
Code example
Java
private void runExample ( GoogleAdsClient googleAdsClient , long customerId , String conversionActionId , String callerId , String callStartDateTime , double conversionValue , Long conversionCustomVariableId , String conversionCustomVariableValue , ConsentStatus adUserDataConsent ) { // Create a call conversion by specifying currency as USD. CallConversion . Builder conversionBuilder = CallConversion . newBuilder () . setConversionAction ( conversionActionId ) . setCallerId ( callerId ) . setCallStartDateTime ( callStartDateTime ) . setConversionValue ( conversionValue ) . setCurrencyCode ( "USD" ); if ( conversionCustomVariableId != null && conversionCustomVariableValue != null ) { conversionBuilder . addCustomVariables ( CustomVariable . newBuilder () . setConversionCustomVariable ( ResourceNames . conversionCustomVariable ( customerId , conversionCustomVariableId )) . setValue ( conversionCustomVariableValue )); } // Sets the consent information, if provided. if ( adUserDataConsent != null ) { // Specifies whether user consent was obtained for the data you are uploading. See // https://www.google.com/about/company/user-consent-policy for details. conversionBuilder . setConsent ( Consent . newBuilder (). setAdUserData ( adUserDataConsent )); } CallConversion conversion = conversionBuilder . build (); // Uploads the call conversion to the API. try ( ConversionUploadServiceClient conversionUploadServiceClient = googleAdsClient . getLatestVersion (). createConversionUploadServiceClient ()) { // Partial failure MUST be enabled for this request. // NOTE: This request contains a single conversion as a demonstration. However, if you have // multiple conversions to upload, it's best to upload multiple conversions per request // instead of sending a separate request per conversion. See the following for per-request // limits: // https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload_service UploadCallConversionsResponse response = conversionUploadServiceClient . uploadCallConversions ( UploadCallConversionsRequest . newBuilder () . setCustomerId ( String . valueOf ( customerId )) . setCustomerId ( Long . toString ( customerId )) . addConversions ( conversion ) . setPartialFailure ( true ) . build ()); // Prints any partial failure errors returned. if ( response . hasPartialFailureError ()) { GoogleAdsFailure googleAdsFailure = ErrorUtils . getInstance (). getGoogleAdsFailure ( response . getPartialFailureError ()); googleAdsFailure . getErrorsList () . forEach ( e - > System . out . println ( "Partial failure occurred: " + e . getMessage ())); throw new RuntimeException ( "Partial failure occurred " + response . getPartialFailureError (). getMessage ()); } // Prints the result if valid. CallConversionResult result = response . getResults ( 0 ); System . out . printf ( "Uploaded call conversion that occurred at '%' for caller ID '%' to the conversion" + " action with resource name '%'.%n" , result . getCallStartDateTime (), result . getCallerId (), result . getConversionAction ()); } }
C#
public void Run ( GoogleAdsClient client , long customerId , long conversionActionId , string callerId , string callStartTime , string conversionTime , double conversionValue , long? conversionCustomVariableId , string conversionCustomVariableValue , ConsentStatus ? adUserDataConsent ) { // Get the ConversionUploadService. ConversionUploadServiceClient conversionUploadService = client . GetService ( Services . V22 . ConversionUploadService ); // Create a call conversion by specifying currency as USD. CallConversion callConversion = new CallConversion () { ConversionAction = ResourceNames . ConversionAction ( customerId , conversionActionId ), CallerId = callerId , CallStartDateTime = callStartTime , ConversionDateTime = conversionTime , ConversionValue = conversionValue , CurrencyCode = "USD" , }; if ( adUserDataConsent != null ) { // Specifies whether user consent was obtained for the data you are uploading. See // https://www.google.com/about/company/user-consent-policy // for details. callConversion . Consent = new Consent () { AdUserData = ( ConsentStatus ) adUserDataConsent }; } if ( conversionCustomVariableId != null && ! string . IsNullOrEmpty ( conversionCustomVariableValue )) { callConversion . CustomVariables . Add ( new CustomVariable () { ConversionCustomVariable = ResourceNames . ConversionCustomVariable ( customerId , conversionCustomVariableId . Value ), Value = conversionCustomVariableValue }); } UploadCallConversionsRequest request = new UploadCallConversionsRequest () { CustomerId = customerId . ToString (), Conversions = { callConversion }, PartialFailure = true }; try { // Issues a request to upload the call conversion. The partialFailure parameter // is set to true, and validateOnly parameter to false as required by this method // call. // NOTE: This request contains a single conversion as a demonstration. However, if // you have multiple conversions to upload, it's best to upload multiple conversions // per request instead of sending a separate request per conversion. See the // following for per-request limits: // https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload_service UploadCallConversionsResponse response = conversionUploadService . UploadCallConversions ( request ); // Since we set partialFailure = true, we can retrieve error messages (if any) from // the operation response. if ( response . PartialFailureError != null ) { Console . WriteLine ( "Call conversion upload failed." ); // Retrieves the errors from the partial failure and prints them. List<GoogleAdsError> errors = response . PartialFailure . GetErrorsByOperationIndex ( 0 ); foreach ( GoogleAdsError error in errors ) { Console . WriteLine ( $"Operation failed with error: {error}." ); } } else { // Prints the result. CallConversionResult uploadedCallConversion = response . Results [ 0 ]; Console . WriteLine ( $"Uploaded call conversion that occurred at " + $"'{uploadedCallConversion.CallStartDateTime}' for caller ID " + $"'{uploadedCallConversion.CallerId}' to the conversion action with " + $"resource name '{uploadedCallConversion.ConversionAction}'." ); } } catch ( GoogleAdsException e ) { Console . WriteLine ( "Failure:" ); Console . WriteLine ( $"Message: {e.Message}" ); Console . WriteLine ( $"Failure: {e.Failure}" ); Console . WriteLine ( $"Request ID: {e.RequestId}" ); throw ; } }
PHP
public static function runExample( GoogleAdsClient $googleAdsClient, int $customerId, int $conversionActionId, string $callerId, string $callStartDateTime, string $conversionDateTime, float $conversionValue, ?string $conversionCustomVariableId, ?string $conversionCustomVariableValue, ?int $adUserDataConsent ) { // Creates a call conversion by specifying currency as USD. $callConversion = new CallConversion([ 'conversion_action' = > ResourceNames::forConversionAction($customerId, $conversionActionId), 'caller_id' => $callerId, 'call_start_date_time' => $callStartDateTime, 'conversion_date_time' => $conversionDateTime, 'conversion_value' => $conversionValue, 'currency_code' => 'USD' ]); if (!is_null($conversionCustomVariableId) && !is_null($conversionCustomVariableValue)) { $callConversion->setCustomVariables([new CustomVariable([ 'conversion_custom_variable' => ResourceNames::forConversionCustomVariable( $customerId, $conversionCustomVariableId ), 'value' => $conversionCustomVariableValue ])]); } // Sets the consent information, if provided. if (!empty($adUserDataConsent)) { // Specifies whether user consent was obtained for the data you are uploading. See // https://www.google.com/about/company/user-consent-policy for details. $callConversion->setConsent(new Consent(['ad_user_data' => $adUserDataConsent])); } // Issues a request to upload the call conversion. $conversionUploadServiceClient = $googleAdsClient->getConversionUploadServiceClient(); // NOTE: This request contains a single conversion as a demonstration. However, if you have // multiple conversions to upload, it's best to upload multiple conversions per request // instead of sending a separate request per conversion. See the following for per-request // limits: // https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload_service $response = $conversionUploadServiceClient->uploadCallConversions( // Partial failure MUST be enabled for this request. UploadCallConversionsRequest::build($customerId, [$callConversion], true) ); // Prints the status message if any partial failure error is returned. // Note: The details of each partial failure error are not printed here, you can refer to // the example HandlePartialFailure.php to learn more. if ($response->hasPartialFailureError()) { printf( "Partial failures occurred: '%s'.%s", $response->getPartialFailureError()->getMessage(), PHP_EOL ); } else { // Prints the result if exists. /** @var CallConversionResult $uploadedCallConversion */ $uploadedCallConversion = $response->getResults()[0]; printf( "Uploaded call conversion that occurred at '%s' for caller ID '%s' to the " . "conversion action with resource name '%s'.%s", $uploadedCallConversion->getCallStartDateTime(), $uploadedCallConversion->getCallerId(), $uploadedCallConversion->getConversionAction(), PHP_EOL ); } }

