You can use the Google Ads API to upload offline click conversions into Google Ads, mapping to the Uploads conversion source in the Google Ads UI, followed by Conversions from clicks. It gives you more flexibility in associating clicks with conversions. You can track ads that led to sales in the offline world, such as over the phone or through a sales rep.
ID parameters
You must enable your website and lead-tracking system to capture and store the
GCLID, the unique ID that Google Ads provides for every impression of a Google ad.
If the click conversions are uploaded in the Google Ads UI,
auto-tagging is automatically
enabled so your website will start receiving the GCLID as a URL parameter.
However, this does not occur when using the Google Ads API, so you should enable
auto-tagging by updating the auto_tagging_enabled
attribute of
Customer
.
Starting with iOS 14,
clicks can contain a wbraid
parameter (for clicks associated with web conversions) or a
gbraid
parameter (for clicks
associated with app conversions) instead of the
gclid
parameter.
Keep in mind the following restrictions when specifying combinations of
wbraid
, gbraid
, gclid
, and
user_identifiers
for a given
ClickConversion
:
Providing both
wbraid
andgbraid
results in the error:GBRAID_WBRAID_BOTH_SET
.Providing
wbraid
orgbraid
while also including agclid
oruser_identifiers
results in the error:VALUE_MUST_BE_UNSET
.Providing neither
wbraid
norgbraid
, but providing bothgclid
anduser_identifiers
: Thegclid
takes precedence.
In addition, custom conversion variables are not supported in combination with
wbraid
or gbraid
. Therefore, providing wbraid
or gbraid
while also
including
custom_variables
results
in the error:
VALUE_MUST_BE_UNSET
.
Code example
You need to associate your offline click conversions with a conversion action by
passing either the gclid
, gbraid
, or wbraid
identifier. In addition,
provide the conversion date time, conversion action resource name and
optionally the conversion value and currency to
ConversionUploadService
:
Java
private void runExample( GoogleAdsClient googleAdsClient, long customerId, long conversionActionId, String gclid, String gbraid, String wbraid, String conversionDateTime, Double conversionValue, Long conversionCustomVariableId, String conversionCustomVariableValue, String orderId) { // Verifies that exactly one of gclid, gbraid, and wbraid is specified, as required. // See https://developers.google.com/google-ads/api/docs/conversions/upload-clicks for details. long numberOfIdsSpecified = Arrays.asList(gclid, gbraid, wbraid).stream().filter(idField -> idField != null).count(); if (numberOfIdsSpecified != 1) { throw new IllegalArgumentException( "Exactly 1 of gclid, gbraid, or wbraid is required, but " + numberOfIdsSpecified + " ID values were provided"); } // Constructs the conversion action resource name from the customer and conversion action IDs. String conversionActionResourceName = ResourceNames.conversionAction(customerId, conversionActionId); // Creates the click conversion. ClickConversion.Builder clickConversionBuilder = ClickConversion.newBuilder() .setConversionAction(conversionActionResourceName) .setConversionDateTime(conversionDateTime) .setConversionValue(conversionValue) .setCurrencyCode("USD"); // Sets the single specified ID field. if (gclid != null) { clickConversionBuilder.setGclid(gclid); } else if (gbraid != null) { clickConversionBuilder.setGbraid(gbraid); } else { clickConversionBuilder.setWbraid(wbraid); } if (conversionCustomVariableId != null && conversionCustomVariableValue != null) { // Sets the custom variable and value, if provided. clickConversionBuilder.addCustomVariables( CustomVariable.newBuilder() .setConversionCustomVariable( ResourceNames.conversionCustomVariable(customerId, conversionCustomVariableId)) .setValue(conversionCustomVariableValue)); } if (orderId != null) { // Sets the order ID (unique transaction ID), if provided. An order ID is required in order to // upload enhancements as shown in the UploadConversionEnhancement example. clickConversionBuilder.setOrderId(orderId); } ClickConversion clickConversion = clickConversionBuilder.build(); // Creates the conversion upload service client. try (ConversionUploadServiceClient conversionUploadServiceClient = googleAdsClient.getLatestVersion().createConversionUploadServiceClient()) { // Uploads the click conversion. Partial failure should always be set to true. UploadClickConversionsResponse response = conversionUploadServiceClient.uploadClickConversions( UploadClickConversionsRequest.newBuilder() .setCustomerId(Long.toString(customerId)) .addConversions(clickConversion) // Enables partial failure (must be true). .setPartialFailure(true) .build()); // Prints any partial errors returned. if (response.hasPartialFailureError()) { GoogleAdsFailure googleAdsFailure = ErrorUtils.getInstance().getGoogleAdsFailure(response.getPartialFailureError()); googleAdsFailure .getErrorsList() .forEach(e -> System.out.println("Partial failure occurred: " + e.getMessage())); } // Prints the result. ClickConversionResult result = response.getResults(0); // Only prints valid results. if (result.hasGclid()) { System.out.printf( "Uploaded conversion that occurred at '%s' from Google Click ID '%s' to '%s'.%n", result.getConversionDateTime(), result.getGclid(), result.getConversionAction()); } } }
C#
public void Run(GoogleAdsClient client, long customerId, long conversionActionId, string gclid, string gbraid, string wbraid, string conversionTime, double conversionValue) { // Get the ConversionActionService. ConversionUploadServiceClient conversionUploadService = client.GetService(Services.V14.ConversionUploadService); // Creates a click conversion by specifying currency as USD. ClickConversion clickConversion = new ClickConversion() { ConversionAction = ResourceNames.ConversionAction(customerId, conversionActionId), ConversionValue = conversionValue, ConversionDateTime = conversionTime, CurrencyCode = "USD" }; // Verifies that exactly one of gclid, gbraid, and wbraid is specified, as required. // See https://developers.google.com/google-ads/api/docs/conversions/upload-clicks // for details. string[] ids = { gclid, gbraid, wbraid }; int idCount = ids.Where(id => !string.IsNullOrEmpty(id)).Count(); if (idCount != 1) { throw new ArgumentException($"Exactly 1 of gclid, gbraid, or wbraid is " + $"required, but {idCount} ID values were provided"); } // Sets the single specified ID field. if (!string.IsNullOrEmpty(gclid)) { clickConversion.Gclid = gclid; } else if (!string.IsNullOrEmpty(wbraid)) { clickConversion.Wbraid = wbraid; } else if (!string.IsNullOrEmpty(gbraid)) { clickConversion.Gbraid = gbraid; } try { // Issues a request to upload the click conversion. UploadClickConversionsResponse response = conversionUploadService.UploadClickConversions( new UploadClickConversionsRequest() { CustomerId = customerId.ToString(), Conversions = { clickConversion }, PartialFailure = true, ValidateOnly = false }); // Prints the result. ClickConversionResult uploadedClickConversion = response.Results[0]; Console.WriteLine($"Uploaded conversion that occurred at " + $"'{uploadedClickConversion.ConversionDateTime}' from Google " + $"Click ID '{uploadedClickConversion.Gclid}' to " + $"'{uploadedClickConversion.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 $gclid, ?string $gbraid, ?string $wbraid, string $conversionDateTime, float $conversionValue, ?string $conversionCustomVariableId, ?string $conversionCustomVariableValue ) { // Verifies that exactly one of gclid, gbraid, and wbraid is specified, as required. // See https://developers.google.com/google-ads/api/docs/conversions/upload-clicks for details. $nonNullFields = array_filter( [$gclid, $gbraid, $wbraid], function ($field) { return !is_null($field); } ); if (count($nonNullFields) !== 1) { throw new \UnexpectedValueException( sprintf( "Exactly 1 of gclid, gbraid or wbraid is required, but %d ID values were " . "provided", count($nonNullFields) ) ); } // Creates a click conversion by specifying currency as USD. $clickConversion = new ClickConversion([ 'conversion_action' => ResourceNames::forConversionAction($customerId, $conversionActionId), 'conversion_value' => $conversionValue, 'conversion_date_time' => $conversionDateTime, 'currency_code' => 'USD' ]); // Sets the single specified ID field. if (!is_null($gclid)) { $clickConversion->setGclid($gclid); } elseif (!is_null($gbraid)) { $clickConversion->setGbraid($gbraid); } else { $clickConversion->setWbraid($wbraid); } if (!is_null($conversionCustomVariableId) && !is_null($conversionCustomVariableValue)) { $clickConversion->setCustomVariables([new CustomVariable([ 'conversion_custom_variable' => ResourceNames::forConversionCustomVariable( $customerId, $conversionCustomVariableId ), 'value' => $conversionCustomVariableValue ])]); } // Issues a request to upload the click conversion. $conversionUploadServiceClient = $googleAdsClient->getConversionUploadServiceClient(); /** @var UploadClickConversionsResponse $response */ $response = $conversionUploadServiceClient->uploadClickConversions( UploadClickConversionsRequest::build($customerId, [$clickConversion], 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 ClickConversionResult $uploadedClickConversion */ $uploadedClickConversion = $response->getResults()[0]; printf( "Uploaded click conversion that occurred at '%s' from Google Click ID '%s' " . "to '%s'.%s", $uploadedClickConversion->getConversionDateTime(), $uploadedClickConversion->getGclid(), $uploadedClickConversion->getConversionAction(), PHP_EOL ); } }
Python
def main( client, customer_id, conversion_action_id, gclid, conversion_date_time, conversion_value, conversion_custom_variable_id, conversion_custom_variable_value, gbraid, wbraid, ): """Creates a click conversion with a default currency of USD. Args: client: An initialized GoogleAdsClient instance. customer_id: The client customer ID string. conversion_action_id: The ID of the conversion action to upload to. gclid: The Google Click Identifier ID. If set, the wbraid and gbraid parameters must be None. conversion_date_time: The the date and time of the conversion (should be after the click time). The format is 'yyyy-mm-dd hh:mm:ss+|-hh:mm', e.g. '2021-01-01 12:32:45-08:00'. conversion_value: The conversion value in the desired currency. conversion_custom_variable_id: The ID of the conversion custom variable to associate with the upload. conversion_custom_variable_value: The str value of the conversion custom variable to associate with the upload. gbraid: The GBRAID for the iOS app conversion. If set, the gclid and wbraid parameters must be None. wbraid: The WBRAID for the iOS app conversion. If set, the gclid and gbraid parameters must be None. """ click_conversion = client.get_type("ClickConversion") conversion_upload_service = client.get_service("ConversionUploadService") conversion_action_service = client.get_service("ConversionActionService") click_conversion.conversion_action = conversion_action_service.conversion_action_path( customer_id, conversion_action_id ) # Sets the single specified ID field. if gclid: click_conversion.gclid = gclid elif gbraid: click_conversion.gbraid = gbraid else: click_conversion.wbraid = wbraid click_conversion.conversion_value = float(conversion_value) click_conversion.conversion_date_time = conversion_date_time click_conversion.currency_code = "USD" if conversion_custom_variable_id and conversion_custom_variable_value: conversion_custom_variable = client.get_type("CustomVariable") conversion_custom_variable.conversion_custom_variable = conversion_upload_service.conversion_custom_variable_path( customer_id, conversion_custom_variable_id ) conversion_custom_variable.value = conversion_custom_variable_value click_conversion.custom_variables.append(conversion_custom_variable) request = client.get_type("UploadClickConversionsRequest") request.customer_id = customer_id request.conversions.append(click_conversion) request.partial_failure = True conversion_upload_response = conversion_upload_service.upload_click_conversions( request=request, ) uploaded_click_conversion = conversion_upload_response.results[0] print( f"Uploaded conversion that occurred at " f'"{uploaded_click_conversion.conversion_date_time}" from ' f'Google Click ID "{uploaded_click_conversion.gclid}" ' f'to "{uploaded_click_conversion.conversion_action}"' )
Ruby
def upload_offline_conversion( customer_id, conversion_action_id, gclid, gbraid, wbraid, conversion_date_time, conversion_value, conversion_custom_variable_id, conversion_custom_variable_value) # GoogleAdsClient will read a config file from # ENV['HOME']/google_ads_config.rb when called without parameters client = Google::Ads::GoogleAds::GoogleAdsClient.new # Verifies that exactly one of gclid, gbraid, and wbraid is specified, as required. # See https://developers.google.com/google-ads/api/docs/conversions/upload-clicks for details. identifiers_specified = [gclid, gbraid, wbraid].reject {|v| v.nil?}.count if identifiers_specified != 1 raise "Must specify exactly one of GCLID, GBRAID, and WBRAID. " \ "#{identifiers_specified} values were provided." end click_conversion = client.resource.click_conversion do |cc| cc.conversion_action = client.path.conversion_action(customer_id, conversion_action_id) # Sets the single specified ID field. if !gclid.nil? cc.gclid = gclid elsif !gbraid.nil? cc.gbraid = gbraid else cc.wbraid = wbraid end cc.conversion_value = conversion_value.to_f cc.conversion_date_time = conversion_date_time cc.currency_code = 'USD' if conversion_custom_variable_id && conversion_custom_variable_value cc.custom_variables << client.resource.custom_variable do |cv| cv.conversion_custom_variable = client.path.conversion_custom_variable( customer_id, conversion_custom_variable_id) cv.value = conversion_custom_variable_value end end end response = client.service.conversion_upload.upload_click_conversions( customer_id: customer_id, conversions: [click_conversion], partial_failure: true, ) if response.partial_failure_error.nil? result = response.results.first puts "Uploaded conversion that occurred at #{result.conversion_date_time} " \ "from Google Click ID #{result.gclid} to #{result.conversion_action}." else failures = client.decode_partial_failure_error(response.partial_failure_error) puts "Request failed. Failure details:" failures.each do |failure| failure.errors.each do |error| puts "\t#{error.error_code.error_code}: #{error.message}" end end end end
Perl
sub upload_offline_conversion { my ( $api_client, $customer_id, $conversion_action_id, $gclid, $gbraid, $wbraid, $conversion_date_time, $conversion_value, $conversion_custom_variable_id, $conversion_custom_variable_value, $order_id ) = @_; # Verify that exactly one of gclid, gbraid, and wbraid is specified, as required. # See https://developers.google.com/google-ads/api/docs/conversions/upload-clicks for details. my $number_of_ids_specified = grep { defined $_ } ($gclid, $gbraid, $wbraid); if ($number_of_ids_specified != 1) { die sprintf "Exactly 1 of gclid, gbraid, or wbraid is required, " . "but %d ID values were provided.\n", $number_of_ids_specified; } # Create a click conversion by specifying currency as USD. my $click_conversion = Google::Ads::GoogleAds::V14::Services::ConversionUploadService::ClickConversion ->new({ conversionAction => Google::Ads::GoogleAds::V14::Utils::ResourceNames::conversion_action( $customer_id, $conversion_action_id ), conversionDateTime => $conversion_date_time, conversionValue => $conversion_value, currencyCode => "USD" }); # Set the single specified ID field. if (defined $gclid) { $click_conversion->{gclid} = $gclid; } elsif (defined $gbraid) { $click_conversion->{gbraid} = $gbraid; } else { $click_conversion->{wbraid} = $wbraid; } if ($conversion_custom_variable_id && $conversion_custom_variable_value) { $click_conversion->{customVariables} = [ Google::Ads::GoogleAds::V14::Services::ConversionUploadService::CustomVariable ->new({ conversionCustomVariable => Google::Ads::GoogleAds::V14::Utils::ResourceNames::conversion_custom_variable( $customer_id, $conversion_custom_variable_id ), value => $conversion_custom_variable_value })]; } if (defined $order_id) { # Set the order ID (unique transaction ID), if provided. An order ID is # required in order to upload enhancements as shown in the # upload_conversion_enhancement.pl example. $click_conversion->{orderId} = $order_id; } # Issue a request to upload the click conversion. my $upload_click_conversions_response = $api_client->ConversionUploadService()->upload_click_conversions({ customerId => $customer_id, conversions => [$click_conversion], partialFailure => "true" }); # Print any partial errors returned. if ($upload_click_conversions_response->{partialFailureError}) { printf "Partial error encountered: '%s'.\n", $upload_click_conversions_response->{partialFailureError}{message}; } # Print the result if valid. my $uploaded_click_conversion = $upload_click_conversions_response->{results}[0]; if (%$uploaded_click_conversion) { printf "Uploaded conversion that occurred at '%s' from Google Click ID '%s' " . "to the conversion action with resource name '%s'.\n", $uploaded_click_conversion->{conversionDateTime}, $uploaded_click_conversion->{gclid}, $uploaded_click_conversion->{conversionAction}; } return 1; }
Import externally attributed conversions
If you use third-party tools or homegrown solutions to track conversions, then you may want to give Google Ads only part of the credit for the conversion. Sometimes, you may also want to distribute a conversion's credit across multiple clicks. Externally attributed conversion imports allow you to upload conversions with fractional credit assigned to each click.
To upload fractional credits, you need to follow the
upload_offline_conversion code example, then specify the
external_attribution_model
and external_attribution_credit
attributes for
the ExternalAttributionData
when
creating the ClickConversion
.
Include cart data in conversions
You can include shopping cart information for a ClickConversion
in the
cart_data
field, which consists
of the following attributes:
merchant_id
: The ID of the associated Merchant Center account.feed_country_code
: The ISO 3166 two-character region code of the Merchant Center feed.feed_language_code
: The ISO 639-1 language code of the Merchant Center feed.local_transaction_cost
: The sum of all transaction-level discounts, in thecurrency_code
of theClickConversion
.items
: The items in the shopping cart.
Each item in items
consists of the following attributes:
product_id
: The ID of the product, sometimes referred to as the offer ID or item ID.quantity
: The quantity of the item.unit_price
: The unit price of the item.
Upload ClickConversion
There are several requirements that must be met when uploading a
ClickConversion
.
To avoid a ConversionUploadError.INVALID_CONVERSION_ACTION
error, the
conversion_action
attribute must refer to a ConversionAction
where:
- The
ConversionActionType
isUPLOAD_CLICKS
. - The
status
of theConversionAction
isENABLED
. - The
ConversionAction
exists in the effective conversion account of the click's Google Ads account. - At the time of the click, conversion tracking was enabled in the effective conversion account of the click's Google Ads account.
- The
customer_id
of theUploadClickConversionsRequest
must be the customer ID of theowner_customer
of each click conversion'sconversion_action
.
In addition, the following conditions must be met:
The
customer_id
of theUploadClickConversionsRequest
must be the customer ID of the effective conversion tracking account of the click's Google Ads account. Otherwise, the conversion upload will result in aConversionUploadError.INVALID_CUSTOMER_FOR_CLICK
error.The
conversion_date_time
must be after the impression happened, to avoid aConversionUploadError.CONVERSION_PRECEDES_GCLID
error.The
conversion_date_time
must be before theclick_through_lookback_window_days
you specified for theConversionAction
, to avoid aConversionUploadError.EXPIRED_GCLID
error.The
conversion_value
must be greater than or equal to zero.The
conversion_date_time
must 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 uploaded 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.
Create ClickConversion
A few things to keep in mind when creating a ClickConversion
:
The
partial_failure
attribute of theUploadClickConversionsRequest
should always be set totrue
. Follow the partial failures guidelines when handling valid and failed operations simultaneously.If you upload a duplicate conversion (that is, a
ClickConversion
with agclid
,conversion_date_time
, andconversion_action
that was previously uploaded), aCLICK_CONVERSION_ALREADY_EXISTS
error is returned.If a single request contains multiple operations for the same conversion, a
DUPLICATE_CLICK_CONVERSION_IN_REQUEST
error is returned.Uploaded conversions are reflected in reports for the impression date of the original click, not the date of the upload request or the date of the
conversion_date_time
of theClickConversion
.If you encounter a
TOO_RECENT_CONVERSION_ACTION
orTOO_RECENT_EVENT
response message to an upload conversion request, wait at least 6 hours after the action creation or event before retrying the failed rows.It takes up to 3 hours for imported conversion statistics to appear in your Google Ads account for last-click attribution. For other search attribution models, it can take longer than 3 hours.
When uploading click conversions for multiple accounts, you can specify the
customer_id
of a common manager account and include conversions with GCLIDs from across multiple accounts. A conversion is attributed to the proper account based on the origin of its GCLID.This approach succeeds only if both of the following conditions are met:
- The effective conversion
account of
the client account of the GCLID is set to the manager specified in
the request's
customer_id
. - The
conversion_action
of theClickConversion
is owned by the manager specified in the request'scustomer_id
.
- The effective conversion
account of
the client account of the GCLID is set to the manager specified in
the request's
When uploading click conversions with cross-account conversion tracking enabled, the conversion action must be in the manager account rather than in the account associated with the GCLID.
If you plan to upload first-party data adjustments for enhanced conversions, you must provide an
order_id
for eachClickConversion
.