Offline Conversion Import
When your conversions happen offline — a sales call that closes a week after the initial ad click, a signed contract that follows a demo request — standard conversion tracking cannot attribute them. Offline conversion import solves this by linking the Google Ads click ID (GCLID) captured at lead time to a conversion recorded in your CRM or ERP.
The overall flow
Section titled “The overall flow”- User clicks your Google Ad → Google appends
?gclid=ABC123to the landing page URL - Your site captures the GCLID and stores it alongside the lead record
- Lead converts offline (sales call, signed contract, in-store purchase)
- You export a CSV or use the API to upload the conversion to Google Ads, referencing the original GCLID
- Google Ads attributes the offline conversion to the original ad click
Step 1 — Capture the GCLID on your landing page
Section titled “Step 1 — Capture the GCLID on your landing page”Read the GCLID from the URL and store it in localStorage for use on the conversion page.
(function() { // Read GCLID from URL parameter var urlParams = new URLSearchParams(window.location.search); var gclid = urlParams.get('gclid');
if (gclid) { // Store in localStorage with expiry (90 days = Google's attribution window) var expiryDate = new Date(); expiryDate.setDate(expiryDate.getDate() + 90);
var gclidData = { value: gclid, expiry: expiryDate.toISOString() };
localStorage.setItem('gclid', JSON.stringify(gclidData));
// Also push to dataLayer so GTM can capture it window.dataLayer = window.dataLayer || []; window.dataLayer.push({ event: 'gclid_captured', gclid: gclid }); }
// On form submit, retrieve stored GCLID function getStoredGclid() { var stored = localStorage.getItem('gclid'); if (!stored) return null; var data = JSON.parse(stored); if (new Date(data.expiry) < new Date()) { localStorage.removeItem('gclid'); return null; } return data.value; }
// Expose for use in form submissions window.getStoredGclid = getStoredGclid;})();Add this as a Custom HTML tag in GTM with an All Pages trigger. It runs silently — only the push fires if a GCLID is present in the URL.
Step 2 — Pass GCLID with your lead form submission
Section titled “Step 2 — Pass GCLID with your lead form submission”When a user submits a lead form, include the GCLID in the data sent to your backend:
async function handleFormSubmit(event) { event.preventDefault();
var gclid = window.getStoredGclid(); var formData = { email: document.getElementById('email').value, phone: document.getElementById('phone').value, message: document.getElementById('message').value, gclid: gclid || null, gclid_captured_at: gclid ? new Date().toISOString() : null, // Include source page landing_page: sessionStorage.getItem('landing_page') || window.location.href };
const response = await fetch('/api/leads', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) });
if (response.ok) { window.dataLayer = window.dataLayer || []; window.dataLayer.push({ event: 'form_submission', form_name: 'contact', has_gclid: !!gclid }); }}Store the gclid field in your CRM or database alongside the lead record.
Step 3 — Configure the Google Ads conversion action
Section titled “Step 3 — Configure the Google Ads conversion action”-
In Google Ads → Tools → Conversions → New Conversion Action
-
Select Import → Other data sources or CRMs
-
Select Track conversions from clicks
-
Name your conversion (e.g., “Signed Contract”, “Qualified Lead”)
-
Set the conversion window (typically 90 days for long sales cycles)
-
Note the Conversion Name and Conversion Action for use in your upload
-
In GTM, create a Data Layer Variable for GCLID storage:
Variable
DLV - gclid(Custom JavaScript):function() {var stored = localStorage.getItem('gclid');if (!stored) return undefined;var data = JSON.parse(stored);return new Date(data.expiry) >= new Date() ? data.value : undefined;}
Step 4 — Upload offline conversions
Section titled “Step 4 — Upload offline conversions”When a lead converts in your CRM, export the conversion data as a CSV:
Google Click ID,Conversion Name,Conversion Time,Conversion Value,Conversion CurrencyTg8AMbkcQw...,Signed Contract,2024-03-15 14:30:00+0000,4500,USDKe2NMpdrBx...,Signed Contract,2024-03-15 17:45:00+0000,7200,USDUpload via Google Ads → Tools → Conversions → [your conversion action] → Upload
Automating the upload with the Google Ads API
Section titled “Automating the upload with the Google Ads API”For high-volume operations, automate via the Google Ads API:
# Python example using google-ads libraryfrom google.ads.googleads.client import GoogleAdsClientfrom google.ads.googleads.errors import GoogleAdsException
def upload_offline_conversions(customer_id, conversions): client = GoogleAdsClient.load_from_storage() service = client.get_service("ConversionUploadService")
click_conversions = [] for conv in conversions: conversion = client.get_type("ClickConversion") conversion.gclid = conv['gclid'] conversion.conversion_action = ( f"customers/{customer_id}/conversionActions/{conv['action_id']}" ) conversion.conversion_date_time = conv['date_time'] conversion.conversion_value = conv['value'] conversion.currency_code = conv.get('currency', 'USD') click_conversions.append(conversion)
request = client.get_type("UploadClickConversionsRequest") request.customer_id = customer_id request.conversions = click_conversions request.partial_failure = True
response = service.upload_click_conversions(request=request) return responseTest it
Section titled “Test it”- Click a Google Ad (use a test campaign if you do not want to spend budget)
- Verify the URL has
?gclid=...appended - Submit your lead form
- Check your backend database — verify the GCLID is stored with the lead
- Create a test CSV with the GCLID, a backdated conversion time, and $1 value
- Upload to Google Ads → verify status shows “Processed”
Common gotchas
Section titled “Common gotchas”GCLID is missing from the URL. Google appends GCLIDs to all ad clicks by default, but URL tracking must be enabled in your campaign settings. Go to Google Ads → Campaign → Settings → Auto-tagging and verify it is enabled.
GCLID disappears after a redirect. If your landing page URL gets redirected (e.g., example.com/lp → example.com/landing-page/), the redirect must preserve the query parameters. Verify your server-side redirects pass all query strings.
90-day upload window. If your sales cycle is longer than 90 days, the conversion cannot be imported. Consider using a longer attribution window in your conversion action settings, up to the maximum your conversion type allows.