Stay organized with collectionsSave and categorize content based on your preferences.
You can use the Google Ads API to uploadoffline click
conversionsinto Google Ads
in order to track ads that led to sales in the offline world, such as over the
phone or through a sales rep.
Setup
There are a few prerequisites to a working offline conversions setup. Make sure
all of the prerequisites are satisfied before proceeding to implementation:
Enable conversion tracking in your Google Ads conversion customer.
Configure tagging and store click IDs.
1. Enable conversion tracking in your Google Ads conversion customer
Thegoogle_ads_conversion_customerfield indicates the Google Ads account that
creates and manages conversions for this customer. For customers usingcross-account conversion tracking,
this is the ID of a manager account. The Google Ads conversion customer ID should be
given as thecustomer_idin Google Ads API requests to create and manage conversions.
Note that this field is populated even if conversion tracking is not enabled.
Theconversion_tracking_statusfield indicates whether conversion tracking is enabled and whether the account
is using cross-account conversion tracking.
Create a conversion action under the Google Ads conversion customer
If theconversion_tracking_statusvalue isNOT_CONVERSION_TRACKED,
conversion tracking is not enabled for the account. Enable conversion tracking
by creating at least oneConversionActionin
the Google Ads conversion account, like in the following example. Alternatively, you
can create a conversion action in the UI by following the instructions in theHelp Centerfor the
conversion type you want to enable.
Note that enhanced conversions are enabled automatically when sent through the
Google Ads API, but they can be disabled through the Google Ads UI.
defmain(client:GoogleAdsClient,customer_id:str)->None:conversion_action_service:ConversionActionServiceClient=(client.get_service("ConversionActionService"))# Create the operation.conversion_action_operation:ConversionActionOperation=client.get_type("ConversionActionOperation")# Create conversion action.conversion_action:ConversionAction=conversion_action_operation.create# Note that conversion action names must be unique. If a conversion action# already exists with the specified conversion_action_name, the create# operation will fail with a ConversionActionError.DUPLICATE_NAME error.conversion_action.name=f"Earth to Mars Cruises Conversion{uuid.uuid4()}"conversion_action.type_=(client.enums.ConversionActionTypeEnum.UPLOAD_CLICKS)conversion_action.category=(client.enums.ConversionActionCategoryEnum.DEFAULT)conversion_action.status=client.enums.ConversionActionStatusEnum.ENABLEDconversion_action.view_through_lookback_window_days=15# Create a value settings object.value_settings:ConversionAction.ValueSettings=(conversion_action.value_settings)value_settings.default_value=15.0value_settings.always_use_default_value=True# Add the conversion action.conversion_action_response:MutateConversionActionsResponse=(conversion_action_service.mutate_conversion_actions(customer_id=customer_id,operations=[conversion_action_operation],))print("Created conversion action "f'"{conversion_action_response.results[0].resource_name}".')
defadd_conversion_action(customer_id)# GoogleAdsClient will read a config file from# ENV['HOME']/google_ads_config.rb when called without parametersclient=Google::Ads::GoogleAds::GoogleAdsClient.new# Add a conversion action.conversion_action=client.resource.conversion_actiondo|ca|ca.name="Earth to Mars Cruises Conversion#{(Time.new.to_f*100).to_i}"ca.type=:UPLOAD_CLICKSca.category=:DEFAULTca.status=:ENABLEDca.view_through_lookback_window_days=15# Create a value settings object.ca.value_settings=client.resource.value_settingsdo|vs|vs.default_value=15vs.always_use_default_value=trueendend# Create the operation.conversion_action_operation=client.operation.create_resource.conversion_action(conversion_action)# Add the ad group ad.response=client.service.conversion_action.mutate_conversion_actions(customer_id:customer_id,operations:[conversion_action_operation],)puts"New conversion action with resource name =#{response.results.first.resource_name}."end
subadd_conversion_action{my($api_client,$customer_id)=@_;# Note that conversion action names must be unique.# If a conversion action already exists with the specified conversion_action_name,# the create operation fails with error ConversionActionError.DUPLICATE_NAME.my$conversion_action_name="Earth to Mars Cruises Conversion #".uniqid();# Create a conversion action.my$conversion_action=Google::Ads::GoogleAds::V21::Resources::ConversionAction->new({name=>$conversion_action_name,category=>DEFAULT,type=>WEBPAGE,status=>ENABLED,viewThroughLookbackWindowDays=>15,valueSettings=>Google::Ads::GoogleAds::V21::Resources::ValueSettings->new({defaultValue=>23.41,alwaysUseDefaultValue=>"true"})});# Create a conversion action operation.my$conversion_action_operation=Google::Ads::GoogleAds::V21::Services::ConversionActionService::ConversionActionOperation->new({create=>$conversion_action});# Add the conversion action.my$conversion_actions_response=$api_client->ConversionActionService()->mutate({customerId=>$customer_id,operations=>[$conversion_action_operation]});printf"New conversion action added with resource name: '%s'.\n",$conversion_actions_response->{results}[0]{resourceName};return1;}
Make sure theconversion_action_typeis set to the correctConversionActionTypevalue.
For more guidance on creating conversion actions in the Google Ads API, seeCreate Conversion Actions.
Retrieve an existing conversion action
You can retrieve details for an existing conversion action by issuing the
following query. Make sure the customer ID in the request is set to the Google Ads
conversion customer you identified above, and the conversion action type is set
to the correctConversionActionTypevalue.
Follow theinstructionsto confirm autotagging is enabled. Set up your Google Ads account, website, and
lead-tracking system to capture and store the GCLID, GBRAID, or WBRAID of each
impression and click for your ads. Autotagging is turned on by default for new
accounts.
Identifies the Google Ads account of your upload. Set this to theGoogle Ads
conversion customerof the account that is the source of the clicks.
job_id
Provides a mechanism for associating your upload requests with the per-job
information inoffline data diagnostics.
If you don't set this field, the Google Ads API assigns each request a unique value
in the range of[2^31, 2^63). If you would prefer to group multiple requests
into a single logical job, set this field to the same value in the range[0, 2^31)on every request in your job.
Thejob_idin theresponsecontains the job ID for the request, regardless of whether you specified a
value or let the Google Ads API assign a value.
partial_failure_enabled
Determines how the Google Ads API handles errors from operations.
Determines error-reporting behavior forenhanced conversions for
leadsuploads. The
Google Ads API ignores this field when handling uploads for click conversions usinggclid,gbraid, orwbraid.
Create click conversion operations
The collection ofClickConversionobjects in
yourUploadClickConversionRequestdefines the set of conversions you want to
upload. Follow this guidance to construct eachClickConversionand set
its fields to appropriate values.
Set the required fields of each conversion operation
Follow these instructions to set the required fields ofClickConversionto appropriate values.
gclid,gbraid,wbraid
The identifier you captured at the time of the click for the conversion's
click or impression. Only setoneof these fields.
conversion_date_time
The date and time of the conversion.
The value must have a timezone specified, and the format must beyyyy-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 doesnothave 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.
You can find more details and examples inthe Help
Centerand check
theCodes and formatsfor a
list of valid timezone IDs.
user_identifiers
Don't set this field when uploading conversions using only click IDs. If this
field is set, Google Ads treats the upload operation as an upload forenhanced
conversions for leads.
conversion_action
The resource name of theConversionActionfor the click conversion.
The conversion action must have atypeofUPLOAD_CLICKS, and must exist
in the Google Ads conversion customer of the Google Ads account associated with the
click.
Review the following list of optional fields and set them on yourClickConversionas needed.
order_id
The transaction ID for the conversion. This field is optional but strongly
recommended. If you set it during the upload, youmustuse it for anyadjustmentsmade to the conversion.
To learn more about how to use a transaction ID to minimize duplicate
conversions, see thisHelp Center article.
external_attribution_data
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, or you
may want to distribute the credit for a conversion across multiple clicks.Externally attributed conversion importsallow you to upload conversions with fractional credit assigned to each click.
To upload fractional credits, set this field to anExternalAttributionDataobject
with values forexternal_attribution_modelandexternal_attribution_credit.
defmain(client:GoogleAdsClient,customer_id:str,conversion_action_id:str,gclid:Optional[str],conversion_date_time:str,conversion_value:str,conversion_custom_variable_id:Optional[str],conversion_custom_variable_value:Optional[str],gbraid:Optional[str],wbraid:Optional[str],order_id:Optional[str],ad_user_data_consent:Optional[str],)->None:"""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 gbraidparameters must be None.conversion_date_time: The the date and time of the conversion (should beafter 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 customvariable to associate with the upload.conversion_custom_variable_value: The str value of the conversion customvariable to associate with the upload.gbraid: The GBRAID for the iOS app conversion. If set, the gclid andwbraid parameters must be None.wbraid: The WBRAID for the iOS app conversion. If set, the gclid andgbraid parameters must be None.order_id: The order ID for the click conversion.ad_user_data_consent: The ad user data consent for the click."""click_conversion:ClickConversion=client.get_type("ClickConversion")conversion_upload_service:ConversionUploadServiceClient=(client.get_service("ConversionUploadService"))conversion_action_service:ConversionActionServiceClient=(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.ifgclid:click_conversion.gclid=gclidelifgbraid:click_conversion.gbraid=gbraidelse:click_conversion.wbraid=wbraidclick_conversion.conversion_value=float(conversion_value)click_conversion.conversion_date_time=conversion_date_timeclick_conversion.currency_code="USD"ifconversion_custom_variable_idandconversion_custom_variable_value:conversion_custom_variable:CustomVariable=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_valueclick_conversion.custom_variables.append(conversion_custom_variable)iforder_id:click_conversion.order_id=order_id# Sets the consent information, if provided.ifad_user_data_consent:# Specifies whether user consent was obtained for the data you are# uploading. For more details, see:# https://www.google.com/about/company/user-consent-policyclick_conversion.consent.ad_user_data=client.enums.ConsentStatusEnum[ad_user_data_consent]# Uploads the click conversion. Partial failure must be set to True here.## NOTE: This request only uploads a single conversion, but if you have# multiple conversions to upload, it's most efficient to upload them in a# single request. See the following for per-request limits for reference:# https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload_servicerequest:UploadClickConversionsRequest=client.get_type("UploadClickConversionsRequest")request.customer_id=customer_idrequest.conversions.append(click_conversion)request.partial_failure=Trueconversion_upload_response:UploadClickConversionsResponse=(conversion_upload_service.upload_click_conversions(request=request,))uploaded_click_conversion:ClickConversionResult=(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}"')
defupload_offline_conversion(customer_id,conversion_action_id,gclid,gbraid,wbraid,conversion_date_time,conversion_value,conversion_custom_variable_id,conversion_custom_variable_value,ad_user_data_consent)# GoogleAdsClient will read a config file from# ENV['HOME']/google_ads_config.rb when called without parametersclient=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?}.countifidentifiers_specified!=1raise"Must specify exactly one of GCLID, GBRAID, and WBRAID. "\"#{identifiers_specified}values were provided."endclick_conversion=client.resource.click_conversiondo|cc|cc.conversion_action=client.path.conversion_action(customer_id,conversion_action_id)# Sets the single specified ID field.if!gclid.nil?cc.gclid=gclidelsif!gbraid.nil?cc.gbraid=gbraidelsecc.wbraid=wbraidendcc.conversion_value=conversion_value.to_fcc.conversion_date_time=conversion_date_timecc.currency_code='USD'ifconversion_custom_variable_id&&conversion_custom_variable_valuecc.custom_variables<<client.resource.custom_variabledo|cv|cv.conversion_custom_variable=client.path.conversion_custom_variable(customer_id,conversion_custom_variable_id)cv.value=conversion_custom_variable_valueendend# Sets the consent information, if provided.unlessad_user_data_consent.nil?cc.consent=client.resource.consentdo|c|# Specifies whether user consent was obtained for the data you are# uploading. For more details, see:# https://www.google.com/about/company/user-consent-policyc.ad_user_data=ad_user_data_consentendendendresponse=client.service.conversion_upload.upload_click_conversions(customer_id:customer_id,# 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_serviceconversions:[click_conversion],partial_failure:true,)ifresponse.partial_failure_error.nil?result=response.results.firstputs"Uploaded conversion that occurred at#{result.conversion_date_time}"\"from Google Click ID#{result.gclid}to#{result.conversion_action}."elsefailures=client.decode_partial_failure_error(response.partial_failure_error)puts"Request failed. Failure details:"failures.eachdo|failure|failure.errors.eachdo|error|puts"\t#{error.error_code.error_code}:#{error.message}"endendendend
subupload_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,$ad_user_data_consent)=@_;# 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){diesprintf"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::V21::Services::ConversionUploadService::ClickConversion->new({conversionAction=>Google::Ads::GoogleAds::V21::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::V21::Services::ConversionUploadService::CustomVariable->new({conversionCustomVariable=>Google::Ads::GoogleAds::V21::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.$click_conversion->{orderId}=$order_id;}# Set the consent information, if provided.if($ad_user_data_consent){# Specify whether user consent was obtained for the data you are uploading.# See https://www.google.com/about/company/user-consent-policy for details.$click_conversion->{consent}=Google::Ads::GoogleAds::V21::Common::Consent->new({adUserData=>$ad_user_data_consent});}# Issue a request to upload the click conversion. Partial failure should# always be set to true.## 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_servicemy$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};}return1;}
Offline data diagnosticsprovide a
single resource for reviewing the overall health of your uploads on an ongoing
basis. However, during implementation you can use the information in this
section to investigate any errors reported in thepartial_failure_errorfield of the response.
Some of the most common errors when uploading conversion actions are
authorization errors, such asUSER_PERMISSION_DENIED. Double check that you
have the customer ID in your request set to the Google Ads conversion customer
which owns the conversion action. Visit ourauthorization guidefor more details and see ourcommon errors guidefor tips on how
to debug these different errors.
The specified conversion action has atypethat is not valid for uploading click conversions. Make sure theConversionActionspecified in your upload request has typeUPLOAD_CLICKS.
The specified conversion action is either not enabled or cannot be found
within the uploadingcustomer_id.Retrieve information about your conversion setupto make sure the conversion action in your upload is enabled and is owned by
thecustomer_idof the upload request.
Thecustomer_idof the request is not the same customer ID
that was theGoogle Ads API conversion accountat the time of the click. Update thecustomer_idof
the request to the correct customer.
Google Ads cannot find the combination of click ID andcustomer_id. Review therequirements forcustomer_idand
confirm you are uploading using the correct Google Ads account.
Multiple conversions in the request have the same combination of click ID,conversion_date_timeandconversion_action. Remove duplicate conversions from your
request.
A conversion with the same combination of click ID,conversion_date_time, andconversion_actionwas
previously uploaded. Ignore this error if you were retrying the upload and this
conversion previously succeeded. If you want to add another conversion in
addition to the previously uploaded conversion, adjust theconversion_date_timeof theClickConversionto avoid
duplicating the previously uploaded conversion.
Google Ads cannot find the combination of click ID andcustomer_id. Review therequirements forcustomer_idand
confirm you are uploading using the correct Google Ads account.
The imported click occurred before the timeframe specified in theclick_through_lookback_window_daysfield. A
change to theclick_through_lookback_window_daysonly impacts clicks
recorded after the change, so changing the lookback window wouldn't
resolve this error for the particular click. If appropriate, change theconversion_actionto another action with a longer lookback
window.
TheClickConversionhas a value set for bothgbraidandwbraid. Update the conversion to use only
one click ID, and make sure you are not combining multiple clicks into the same
conversion. Each click has only one click ID.
Check thelocationof theGoogleAdsErrorto determine which of the following issues led to
the error.
TheClickConversionhas a value set forgclidas
well as at least one ofgbraidorwbraid. Update the
conversion to use only one click ID, and make sure you are not combining
multiple clicks into the same conversion. Each click has only one click
ID.
TheClickConversionhas a value set for eithergbraidorwbraidand has a value forcustom_variables. Google Ads does not support custom variables for a
conversion with agbraidorwbraidclick ID. Unset
thecustom_variablesfield of the conversion.
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. Consult thedata freshness
guidefor more information.
[[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2025-09-03 UTC."],[],[],null,[]]