Stay organized with collectionsSave and categorize content based on your preferences.
You can use the Google Ads API to import offline conversions into 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.
In order to fully take advantage of the benefits of importing conversion data,
we recommend implementing enhanced conversions for leads, which leverages GCLIDs
and user-provided data to maximize durability and performance.
Enhanced conversions
Enhanced conversions help you to improve the accuracy of your conversion
measurement by supplementing your conversions with first-party conversion data,
like email address, name, home address, and phone number.
There are two types of enhanced conversions. For more details see theAbout
enhanced conversionsHelp
Center article.
The following section explains how to enhanceofflineconversions, a feature
also referred to asenhanced conversions for leads.
What is enhanced conversions for leads?
Enhanced conversions for leads is an upgraded version of offline conversion
import that uses user-provided data, such as email addresses, to supplement
imported offline conversion data to improve accuracy and bidding performance.
When you import your offline conversions, the provided hashed customer data is
used to attribute back to the Google Ads campaign by matching to the same data
collected on your website, such as a, lead form, and to signed-in customers
who engaged with your ad. For more information about how enhanced conversions
for leads works, review our article;About enhanced conversions for leads.
There are two ways to implement enhanced conversions for leads depending on
whether you use theGoogle tagto track form submission events on your webpage. For the best performance and
durability, we strongly recommend using Google tag for enhanced conversions for
leads.
If you are starting from scratch, start with thePrerequisitessection.
If you already have offline conversion imports set up, and want to upgrade to
enhanced conversions for leads, we recommend starting with theConfigure taggingsection.
If you've already set up the Google tag, or don't plan to use the Google tag,
and are starting work on your Google Ads API integration, jump to theAPI Implementationsection.
You must opt in to enhanced conversions for leads and accept the customer data
terms before you can use enhanced conversions for leads. You can verify whether
these prerequisites are already met by issuing the following query to the Google Ads
conversion customer:
If either ofaccepted_customer_data_termsorenhanced_conversions_for_leads_enabledisfalse, follow the instructions in
theHelp Centerto
complete these prerequisites.
Configure tagging
Configure the Google tag to enable enhanced conversions for leads by following
the instructions inHelp
Center. To set up
enhanced conversions for leads with Google Tag Manager, follow theseinstructions.
API Implementation
Here is the overall flow for importing enhanced conversions for leads using the
Google Ads API.
defnormalize_and_hash_email_address(email_address:str)->str:"""Returns the result of normalizing and hashing an email address.For this use case, Google Ads requires removal of any '.' characterspreceding "gmail.com" or "googlemail.com"Args:email_address: An email address to normalize.Returns:A normalized (lowercase, removed whitespace) and SHA-265 hashed string."""normalized_email:str=email_address.strip().lower()email_parts:list[str]=normalized_email.split("@")# Check that there are at least two segmentsiflen(email_parts)>1:# Checks whether the domain of the email address is either "gmail.com"# or "googlemail.com". If this regex does not match then this statement# will evaluate to None.ifre.match(r"^(gmail|googlemail)\.com$",email_parts[1]):# Removes any '.' characters from the portion of the email address# before the domain if the domain is gmail.com or googlemail.com.email_parts[0]=email_parts[0].replace(".","")normalized_email="@".join(email_parts)returnnormalize_and_hash(normalized_email)defnormalize_and_hash(s:str)->str:"""Normalizes and hashes a string with SHA-256.Private customer data must be hashed during upload, as described at:https://support.google.com/google-ads/answer/7474263Args:s: The string to perform this operation on.Returns:A normalized (lowercase, removed whitespace) and SHA-256 hashed string."""returnhashlib.sha256(s.strip().lower().encode()).hexdigest()
# Returns the result of normalizing and then hashing the string using the# provided digest. Private customer data must be hashed during upload, as# described at https://support.google.com/google-ads/answer/7474263.defnormalize_and_hash(str)# Remove leading and trailing whitespace and ensure all letters are lowercase# before hashing.Digest::SHA256.hexdigest(str.strip.downcase)end# Returns the result of normalizing and hashing an email address. For this use# case, Google Ads requires removal of any '.' characters preceding 'gmail.com'# or 'googlemail.com'.defnormalize_and_hash_email(email)email_parts=email.downcase.split("@")# Removes any '.' characters from the portion of the email address before the# domain if the domain is gmail.com or googlemail.com.ifemail_parts.last=~/^(gmail|googlemail)\.com\s*/email_parts[0]=email_parts[0].gsub('.','')endnormalize_and_hash(email_parts.join('@'))end
subnormalize_and_hash{my$value=shift;# Removes leading, trailing, and intermediate spaces.$value=~s/\s+//g;returnsha256_hex(lc$value);}# Returns the result of normalizing and hashing an email address. For this use# case, Google Ads requires removal of any '.' characters preceding 'gmail.com'# or 'googlemail.com'.subnormalize_and_hash_email_address{my$email_address=shift;my$normalized_email=lc$email_address;my@email_parts=split('@',$normalized_email);if(scalar@email_parts>1&&$email_parts[1]=~/^(gmail|googlemail)\.com\s*/){# Remove any '.' characters from the portion of the email address before the# domain if the domain is 'gmail.com' or 'googlemail.com'.$email_parts[0]=~s/\.//g;$normalized_email=sprintf'%s@%s',$email_parts[0],$email_parts[1];}returnnormalize_and_hash($normalized_email);}
The collection ofClickConversionobjects in
yourUploadClickConversionRequestrepresents the set of conversions you want
to import. Keep the following details in mind when creatingClickConversionobjects:
gclid
GCLIDsare click identifiers
that are captured from URL parameters when an individual clicks on your ad
and navigates to your website.
user_identifiers
When using enhanced conversions for leads, youmustpopulate theuser_identifiersfield
with normalized and hashed user-provided data. If you have multiple user
identifiers available, create a separateUserIdentifierfor each one,
up to five identifiers.
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 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.
You can find more details and examples inthe Help
Centerand check
theCodes and formatsfor a
list of valid timezone IDs.
conversion_action
The resource name of theConversionActionfor the offline 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.
It'shighlyrecommended that you populate theconsentfield of theClickConversionobject. If not set, it's possible that your conversions
won't be attributable.
order_id
Also known as the transaction ID for the conversion. This field is optional,
but strongly recommended, because it makes it simpler to reference imported
conversions when makingadjustments.
If you set it during the import, you must use it for any adjustments. To
learn more about how to use a transaction ID to minimize duplicate
conversions, see thisHelp Center article.
Session attributes represent aggregated identifiers used for conversion
attribution. These work in addition to click identifiers, (such as GCLIDs and
GBRAIDs), and user-provided data, which is central to enhanced conversions for
leads. There are two ways to import session attributes: by providing the
encoded token generated by our Javascript code in the browser, or by providing
individual key value pairs for each of the identifiers.
To maximize your campaign performance, we recommend importing click
identifiers, user-provided data, and session attributes with all of your
conversions, if possible.
The IP address of the customer when they arrived on the landing page after an
ad click and before a conversion event. This is the IP address of the
customer's device, not the advertiser's server.
This field is a string representing an IP address in eitherIPv4orIPv6format. For example:
IPv4:"192.0.2.0"
IPv6:"2001:0DB8:1234:5678:9999:1111:0000:0001"
Code example
This example shows how to set your normalized and hashed user-provided data onto
aClickConversionobject.
# Extract user email and phone from the raw data, normalize and hash it,# then wrap it in UserIdentifier objects. Create a separate UserIdentifier# object for each. The data in this example is hardcoded, but in your# application you might read the raw data from an input file.# IMPORTANT: Since the identifier attribute of UserIdentifier# (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)# is a oneof# (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must# set only ONE of hashed_email, hashed_phone_number, mobile_id,# third_party_user_id, or address_info. Setting more than one of these# attributes on the same UserIdentifier will clear all the other members of# the oneof. For example, the following code is INCORRECT and will result in# a UserIdentifier with ONLY a hashed_phone_number:## incorrectly_populated_user_identifier = client.get_type("UserIdentifier")# incorrectly_populated_user_identifier.hashed_email = "...""# incorrectly_populated_user_identifier.hashed_phone_number = "...""raw_record:Dict[str,Union[str,float]]={# Email address that includes a period (.) before the Gmail domain."email":"alex.2@example.com",# Phone number to be converted to E.164 format, with a leading '+' as# required."phone":"+1 800 5550102",# This example lets you input conversion details as arguments,# but in reality you might store this data alongside other user data,# so we include it in this sample user record."order_id":order_id,"gclid":gclid,"conversion_action_id":conversion_action_id,"conversion_date_time":conversion_date_time,"conversion_value":conversion_value,"currency_code":"USD","ad_user_data_consent":ad_user_data_consent,}# Constructs the click conversion.click_conversion:ClickConversion=client.get_type("ClickConversion")# Creates a user identifier using the hashed email address, using the# normalize and hash method specifically for email addresses.email_identifier:UserIdentifier=client.get_type("UserIdentifier")# Optional: Specifies the user identifier source.email_identifier.user_identifier_source=(client.enums.UserIdentifierSourceEnum.FIRST_PARTY)# Uses the normalize and hash method specifically for email addresses.email_identifier.hashed_email=normalize_and_hash_email_address(raw_record["email"])# Adds the user identifier to the conversion.click_conversion.user_identifiers.append(email_identifier)# Checks if the record has a phone number, and if so, adds a UserIdentifier# for it.ifraw_record.get("phone")isnotNone:phone_identifier:UserIdentifier=client.get_type("UserIdentifier")phone_identifier.hashed_phone_number=normalize_and_hash(raw_record["phone"])# Adds the phone identifier to the conversion adjustment.click_conversion.user_identifiers.append(phone_identifier)
# Extract user email and phone from the raw data, normalize and hash it,# then wrap it in UserIdentifier objects. Create a separate UserIdentifier# object for each. The data in this example is hardcoded, but in your# application you might read the raw data from an input file.# IMPORTANT: Since the identifier attribute of UserIdentifier# (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)# is a oneof# (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must# set only ONE of hashed_email, hashed_phone_number, mobile_id,# third_party_user_id, or address_info. Setting more than one of these# attributes on the same UserIdentifier will clear all the other members of# the oneof. For example, the following code is INCORRECT and will result in# a UserIdentifier with ONLY a hashed_phone_number:## incorrectly_populated_user_identifier.hashed_email = "...""# incorrectly_populated_user_identifier.hashed_phone_number = "...""raw_record={# Email address that includes a period (.) before the Gmail domain."email"=>"alex.2@example.com",# Phone number to be converted to E.164 format, with a leading '+' as# required."phone"=>"+1 800 5550102",# This example lets you input conversion details as arguments,# but in reality you might store this data alongside other user data,# so we include it in this sample user record."order_id"=>order_id,"gclid"=>gclid,"conversion_action_id"=>conversion_action_id,"conversion_date_time"=>conversion_date_time,"conversion_value"=>conversion_value,"currency_code"=>"USD","ad_user_data_consent"=>ad_user_data_consent,"session_attributes_encoded"=>session_attributes_encoded,"session_attributes_hash"=>session_attributes_hash}click_conversion=client.resource.click_conversiondo|cc|cc.conversion_action=client.path.conversion_action(customer_id,conversion_action_id)cc.conversion_date_time=conversion_date_timecc.conversion_value=conversion_value.to_fcc.currency_code='USD'unlessorder_id.nil?cc.order_id=order_idendunlessraw_record["gclid"].nil?cc.gclid=gclidend# Specifies whether user consent was obtained for the data you are# uploading. For more details, see:# https://www.google.com/about/company/user-consent-policyunlessraw_record["ad_user_data_consent"].nil?cc.consent=client.resource.consentdo|c|c.ad_user_data=ad_user_data_consentendend# Set one of the session_attributes_encoded or# session_attributes_key_value_pairs fields if either are provided.ifsession_attributes_encoded!=nilcc.class.module_eval{attr_accessor:session_attributes_encoded}cc.session_attributes_encoded=session_attributes_encodedelsifsession_attributes_hash!=nil# Add new attribute to click conversion objectcc.class.module_eval{attr_accessor:session_attributes_key_value_pairs}cc.session_attributes_key_value_pairs=::Google::Ads::GoogleAds::V19::Services::SessionAttributesKeyValuePairs.new# Loop thru inputted session_attributes_hash to populate session_attributes_key_value_pairssession_attributes_hash.eachdo|key,value|pair=::Google::Ads::GoogleAds::V19::Services::SessionAttributeKeyValuePair.newpair.session_attribute_key=keypair.session_attribute_value=valuecc.session_attributes_key_value_pairs.key_value_pairs<<pairendend# Creates a user identifier using the hashed email address, using the# normalize and hash method specifically for email addresses.# If using a phone number, use the normalize_and_hash method instead.cc.user_identifiers<<client.resource.user_identifierdo|ui|ui.hashed_email=normalize_and_hash_email(raw_record["email"])# Optional: Specifies the user identifier source.ui.user_identifier_source=:FIRST_PARTYend# Checks if the record has a phone number, and if so, adds a UserIdentifier# for it.unlessraw_record["phone"].nil?cc.user_identifiers<<client.resource.user_identifierdo|ui|ui.hashed_phone_number=normalize_and_hash(raw_record["phone"])endendend
# Create an empty click conversion.my$click_conversion=Google::Ads::GoogleAds::V21::Services::ConversionUploadService::ClickConversion->new({});# Extract user email and phone from the raw data, normalize and hash it,# then wrap it in UserIdentifier objects. Create a separate UserIdentifier# object for each.# The data in this example is hardcoded, but in your application# you might read the raw data from an input file.## IMPORTANT: Since the identifier attribute of UserIdentifier# (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)# is a oneof# (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set# only ONE of hashed_email, hashed_phone_number, mobile_id, third_party_user_id,# or address-info. Setting more than one of these attributes on the same UserIdentifier# will clear all the other members of the oneof. For example, the following code is# INCORRECT and will result in a UserIdentifier with ONLY a hashed_phone_number:## my $incorrect_user_identifier = Google::Ads::GoogleAds::V21::Common::UserIdentifier->new({# hashedEmail => '...',# hashedPhoneNumber => '...',# });my$raw_record={# Email address that includes a period (.) before the Gmail domain.email=>'alex.2@example.com',# Phone number to be converted to E.164 format, with a leading '+' as# required.phone=>'+1 800 5550102',# This example lets you input conversion details as arguments,# but in reality you might store this data alongside other user data,# so we include it in this sample user record.orderId=>$order_id,gclid=>$gclid,conversionActionId=>$conversion_action_id,conversionDateTime=>$conversion_date_time,conversionValue=>$conversion_value,currencyCode=>"USD",adUserDataConsent=>$ad_user_data_consent};my$user_identifiers=[];# Create a user identifier using the hashed email address, using the normalize# and hash method specifically for email addresses.my$hashed_email=normalize_and_hash_email_address($raw_record->{email});push(@$user_identifiers,Google::Ads::GoogleAds::V21::Common::UserIdentifier->new({hashedEmail=>$hashed_email,# Optional: Specify the user identifier source.userIdentifierSource=>FIRST_PARTY}));# Create a user identifier using normalized and hashed phone info.my$hashed_phone=normalize_and_hash($raw_record->{phone});push(@$user_identifiers,Google::Ads::GoogleAds::V21::Common::UserIdentifier->new({hashedPhone=>$hashed_phone,# Optional: Specify the user identifier source.userIdentifierSource=>FIRST_PARTY}));# Add the user identifiers to the conversion.$click_conversion->{userIdentifiers}=$user_identifiers;
# Add details of the conversion.# Gets the conversion action resource name.conversion_action_service:ConversionActionServiceClient=(client.get_service("ConversionActionService"))click_conversion.conversion_action=(conversion_action_service.conversion_action_path(customer_id,raw_record["conversion_action_id"]))click_conversion.conversion_date_time=raw_record["conversion_date_time"]click_conversion.conversion_value=raw_record["conversion_value"]click_conversion.currency_code=raw_record["currency_code"]# Sets the order ID if provided.ifraw_record.get("order_id"):click_conversion.order_id=raw_record["order_id"]# Sets the gclid if provided.ifraw_record.get("gclid"):click_conversion.gclid=raw_record["gclid"]# Specifies whether user consent was obtained for the data you are# uploading. For more details, see:# https://www.google.com/about/company/user-consent-policyifraw_record["ad_user_data_consent"]:click_conversion.consent.ad_user_data=client.enums.ConsentStatusEnum[raw_record["ad_user_data_consent"]]# Set one of the session_attributes_encoded or# session_attributes_key_value_pairs fields if either are provided.ifsession_attributes_encoded:click_conversion.session_attributes_encoded=session_attributes_encodedelifsession_attributes_dict:forkey,valueinsession_attributes_dict.items():pair:SessionAttributeKeyValuePair=client.get_type("SessionAttributeKeyValuePair")pair.session_attribute_key=keypair.session_attribute_value=valueclick_conversion.session_attributes_key_value_pairs.key_value_pairs.append(pair)
cc.conversion_action=client.path.conversion_action(customer_id,conversion_action_id)cc.conversion_date_time=conversion_date_timecc.conversion_value=conversion_value.to_fcc.currency_code='USD'unlessorder_id.nil?cc.order_id=order_idendunlessraw_record["gclid"].nil?cc.gclid=gclidend# Specifies whether user consent was obtained for the data you are# uploading. For more details, see:# https://www.google.com/about/company/user-consent-policyunlessraw_record["ad_user_data_consent"].nil?cc.consent=client.resource.consentdo|c|c.ad_user_data=ad_user_data_consentendend# Set one of the session_attributes_encoded or# session_attributes_key_value_pairs fields if either are provided.ifsession_attributes_encoded!=nilcc.class.module_eval{attr_accessor:session_attributes_encoded}cc.session_attributes_encoded=session_attributes_encodedelsifsession_attributes_hash!=nil# Add new attribute to click conversion objectcc.class.module_eval{attr_accessor:session_attributes_key_value_pairs}cc.session_attributes_key_value_pairs=::Google::Ads::GoogleAds::V19::Services::SessionAttributesKeyValuePairs.new# Loop thru inputted session_attributes_hash to populate session_attributes_key_value_pairssession_attributes_hash.eachdo|key,value|pair=::Google::Ads::GoogleAds::V19::Services::SessionAttributeKeyValuePair.newpair.session_attribute_key=keypair.session_attribute_value=valuecc.session_attributes_key_value_pairs.key_value_pairs<<pairendend
# Add details of the conversion.$click_conversion->{conversionAction}=Google::Ads::GoogleAds::V21::Utils::ResourceNames::conversion_action($customer_id,$raw_record->{conversionActionId});$click_conversion->{conversionDateTime}=$raw_record->{conversionDateTime};$click_conversion->{conversionValue}=$raw_record->{conversionValue};$click_conversion->{currencyCode}=$raw_record->{currencyCode};# Set the order ID if provided.if(defined$raw_record->{orderId}){$click_conversion->{orderId}=$raw_record->{orderId};}# Set the Google click ID (gclid) if provided.if(defined$raw_record->{gclid}){$click_conversion->{gclid}=$raw_record->{gclid};}# Set the consent information, if provided.if(defined$raw_record->{adUserDataConsent}){$click_conversion->{consent}=Google::Ads::GoogleAds::V21::Common::Consent->new({adUserData=>$raw_record->{adUserDataConsent}});}# Set one of the session_attributes_encoded or session_attributes_key_value_pairs# fields if either are provided.if(defined$session_attributes_encoded){$click_conversion->{sessionAttributesEncoded}=$session_attributes_encoded;}elsif(defined$session_attributes_hash){while(my($key,$value)=each%$session_attributes_hash){my$pair=Google::Ads::GoogleAds::V21::Services::ConversionUploadService::SessionAttributeKeyValuePair->new({sessionAttributeKey=>$key,sessionAttributeValue=>$value});push@{$click_conversion->{sessionAttributesKeyValuePairs}{keyValuePairs}},$pair;}}
Once yourClickConversionobjects are configured and added to theconversionsfield of theUploadClickConversionRequestobject, set the
following fields and pass the request to theUploadClickConversionsmethod
on theConversionUploadService.
customer_id
Set this to theGoogle Ads conversion customerof the account that is the
source of the clicks. If you're unsure which account is the correct one, refer
to thecustomer.conversion_tracking_setting.google_ads_conversion_customerfield in the example query in thegetting startedsection.
job_id
Provides a mechanism for associating your import 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
This field must be set totruewhen importing conversions. Follow thepartial failures guidelineswhen
processing the response.
Import the request
Once you've populated yourClickConversionobjects, and constructed your
request, you can submit your import.
# Creates the conversion upload service client.conversion_upload_service:ConversionUploadServiceClient=(client.get_service("ConversionUploadService"))# Uploads the click conversion. Partial failure should always be set to# True.# 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_serviceresponse:UploadClickConversionsResponse=(conversion_upload_service.upload_click_conversions(customer_id=customer_id,conversions=[click_conversion],# Enables partial failure (must be true).partial_failure=True,))
response=client.service.conversion_upload.upload_click_conversions(customer_id:customer_id,conversions:[click_conversion],# Partial failure must be true.partial_failure:true,)ifresponse.partial_failure_errorputs"Partial failure encountered:#{response.partial_failure_error.message}"elseresult=response.results.firstputs"Uploaded click conversion that happened at#{result.conversion_date_time}"\"to#{result.conversion_action}."end
# 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$response=$api_client->ConversionUploadService()->upload_click_conversions({customerId=>$customer_id,conversions=>[$click_conversion],# Enable partial failure (must be true).partialFailure=>"true"});
It takes up to three 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 three
hours. Consult thedata freshness
guidefor more information.
When reporting on conversion metrics for your campaigns, refer toMapping user
interface metricsto correlate Google Ads UI
metrics with Google Ads API reporting fields. You can also query theconversion_actionresource to view the total
number of conversions and the total conversion value for a given conversion
action.
Best practices
Keep the following best practices in mind when implementing enhanced conversions
for leads.
Send all conversion data regardless of completeness
To ensure full and accurate conversion reporting, import all available offline
conversion events, including those that might not have come from Google Ads.
Conversions that include only user-provided data are still useful and can
contribute positively to Google Ads campaign optimization.
If you assign anorder_idto a conversion, we recommend including it.
If you have the GCLID for a conversion, we recommend sending it in addition to
theuser_identifiersfor improved performance. Furthermore, if you
have more than oneUserIdentifierfor the conversion, include all of them on
theClickConversionobject to improve the likelihood of a match.
Batch multiple conversions in a single request
If you have multiple conversions to import, batch the conversions into oneUploadClickConversionsRequest,
rather than sending an import request per conversion.
Check the quotaguidefor limits on the number of conversions per request.
If you wantoffline data diagnosticsto
group a set of requests under the same logical job, set thejob_idof all the
requests to the same value. This can be useful if you have a single job or
process that imports a large number of conversions using multiple requests. If
you set thejob_idon each of those requests to the same value, then you can
retrieve a single entry for the job fromjob_summaries.
If instead you let the Google Ads API assign a system-generated value to thejob_idof each request, thejob_summariescontains a separate entry for each request,
which could make analyzing the overall health of your job more challenging.
Don't use external attribution data
When using enhanced conversions for leads, don't setexternal_attribution_dataon theClickConversionor specify aconversion_actionthat uses an external
attribution model. Google Ads doesn't support externally attributed conversions for
imports using user-provided data.
Don't include custom variables
When using enhanced conversions for leads, don't include anycustom_variables.
Google Ads doesn't support the use of custom variables with user-provided data in
conversion imports. If custom variables are included with conversions that
contain user-provided data, those conversions will be considered invalid and
dropped.
Troubleshooting
Offline data diagnosticsprovide a
single resource for reviewing the overall health of your imports 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 importing 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 is either not enabled, or cannot be accessed by the client
account specified by the `client_id` field in the request. Make sure the conversion action
in your upload is enabled and is owned by the customer sending the upload request.
This error may also occur if the GCLID in the request belongs to a client account that does
not have access to the conversion action specified in the request. You can verify whether a
GCLID belongs to a client account using theclick_viewresource, by submitting a query
that filters byclick_view.gclidandsegments.date, where the date is the date the click occurred.
The specified conversion action has a type that is not valid for enhanced
conversions for leads. Make sure theConversionActionspecified in
your upload request has typeUPLOAD_CLICKS.
No click was found that matched the provided user identifiers. The Google Ads API
only returns this error ifdebug_enabledistrueon
theUploadClickConversionsRequest.
If a conversion encounters this warning, the Google Ads API
includes it in thesuccessful_event_countof youroffline data diagnostics. The
Google Ads API includes an entry forCLICK_NOT_FOUNDin thealertscollection so you can monitor the frequency of this warning.
This error is expected if the click is not from a Google Ads campaign. For example it may come
fromSA360orDV360. Other
possible causes are as follows:
In rare instances where the uploading customer is different from the Google Ads
conversion customer, this error can mean that the uploading customer has
accepted the customer data terms, but the serving customer has not.
You can determine if an
account has accepted the customer data terms by querying thecustomerresource and checking thecustomer.offline_conversion_tracking_info.accepted_customer_data_termsfield.
[[["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,[]]