When working with Google Ads, most advertisers usually default to using the Auto-tagging feature which automatically appends the gclid parameter to the destination URL (e.g. domain.com/landing-page?gclid=12345abcde). This feature enables the tracking mechanisms used by Google Ads and Google Analytics that can match a specific visitor and their consecutive actions with the ad clicked.
In layman’s terms, the click ID (i.e. gclid) pairs the anonymous visitor with the ad clicked.
Why use UTM’s instead of only using GCLID?
If you’re using Google Analytics as your primary reporting platform, this makes perfect sense. But in some cases, you might still require the UTM tracking. Let’s consider these examples:
If you’re using Google Analytics as your primary reporting platform, this makes perfect sense. But in some cases, you might still require the UTM tracking. Let’s consider these examples:
- Additional analytics platform – If you’re using a tool other than Google Analytics (or even in parallel to), you will want it to report accurately on the campaigns, as it won’t fetch the same Google Ads data.
- Marketing Automation platforms – While some platforms are able to fetch the Google Ads data (e.g. Hubspot), most platforms will struggle with this, and require the UTM’s to appear explicitly in the URL.
- CRM Platforms – Some CRM platforms, e.g. Salesforce, are disconnected from the page level tracking, so to enable the data capture of the user’s source, campaign data is sent using hidden fields in forms. This too requires the UTM’s to be present.
- BI Platforms – Similar to the above, these too don’t always have this integration and require the UTM’s to be present.
Doesn’t Google Ads have a solution?
The intuitive solution to this should have been using Google Ad’s ValueTrack parameters. These parameters can be automatically appended to all landing page URLs, injecting static (e.g. utm_source=google) or dynamic (e.g. utm_keyword={keyword}) content to the URL.
However, Google Ads allows only for the Campaign ID and Ad Group ID to be used as parameters for ValueTrack.
Though this might make sense, as Campaign/Ad Group ID are static, whereas the name can change, this is far from being a human-friendly way of analyzing your data.
The solution: Google Ads Script
Google Ads Scripts are a great solution for automating complex tasks in your account. (see other examples here). These code snippets can interact with other Google tools, e.g. Google Sheets, and even with external tools such as writing/reading from a SQL server.
This is usually a tool for technical savvy marketers, so we tried to keep this as simple as possible.
The code
function main() {
var SuffixTemplate="utm_source=google&utm_medium=cpc&utm_campaign={CampaignName}&utm_content={AdGroupName}&utm_ad={creative}&utm_term={keyword}&matchtype={matchtype}&device={device}&GeoLoc={loc_physical_ms}&placement={placement}&network={network}&campaign_id={campaignid}&adset_id={adgroupid}&ad_id={creative}";
var _CAMPAIGN_CONTAINS=""; //Filter by Campaign name
var _ADGROUP_CONTAINS=""; //Filter by Adgroup name
var STATUS="ENABLED"; //ENABLED, PAUSED
if (SuffixTemplate.search(/{AdGroupName}|{CampaignName}/g)==-1) {
Logger.log("Enter at least one of the {CampaignName} or {AdGroupName} parameter in the tracking template");
return
}
if (SuffixTemplate.search("{CampaignName}")>0&&SuffixTemplate.search("{AdGroupName}")==-1) {
var campaignIterator=_CAMPAIGN_CONTAINS==""?AdsApp.campaigns().withCondition("Status = "+STATUS).get():AdsApp.campaigns().withCondition("Name contains '"+_CAMPAIGN_CONTAINS+"'").withCondition("Status = "+STATUS).get();
if (!campaignIterator.hasNext()) {
Logger.log("No Campaigns matched with this condition");
return
}
while (campaignIterator.hasNext()) {
var campaign=campaignIterator.next();
var campaigntemplate=SuffixTemplate.replace(/{CampaignName}/g,campaign.getName().replace(/\s/g,'%20'))
campaign.urls().setFinalUrlSuffix(campaigntemplate);
Logger.log(campaign.getName()+" => "+campaigntemplate)
}
}
if (SuffixTemplate.search("{AdGroupName}")>0) {
var adgroupIterator = {hasNext:function(){return false}}
if (_ADGROUP_CONTAINS==""&&_CAMPAIGN_CONTAINS=="") {
adgroupIterator=AdsApp.adGroups().withCondition("Status = "+STATUS).get();
} else if (_ADGROUP_CONTAINS==""&&_CAMPAIGN_CONTAINS!=="") {
adgroupIterator=AdsApp.adGroups().withCondition("CampaignName contains '"+_CAMPAIGN_CONTAINS+"'").withCondition("Status = "+STATUS).get();
} else if (_ADGROUP_CONTAINS!==""&&_CAMPAIGN_CONTAINS!=="") {
adgroupIterator=AdsApp.adGroups().withCondition("CampaignName contains '"+_CAMPAIGN_CONTAINS+"'").withCondition("Name contains '"+_ADGROUP_CONTAINS+"'").withCondition("Status = "+STATUS).get();
} else if (_ADGROUP_CONTAINS!==""&&_CAMPAIGN_CONTAINS=="") {
adgroupIterator=AdsApp.adGroups().withCondition("Name contains '"+_ADGROUP_CONTAINS+"'").withCondition("Status = "+STATUS).get();
}
if (!adgroupIterator.hasNext()) {
Logger.log("No Campaigns/Adgroups matched with this condition");return
}
while (adgroupIterator.hasNext()) {
var adgroup=adgroupIterator.next();
var adgrouptemplate=SuffixTemplate.replace(/{AdGroupName}/g,adgroup.getName().replace(/\s/g,'%20'))
if (SuffixTemplate.search("{CampaignName}")>0) {
adgrouptemplate=adgrouptemplate.replace(/{CampaignName}/g,adgroup.getCampaign().getName().replace(/\s/g,'%20'))
}
adgroup.urls().setFinalUrlSuffix(adgrouptemplate);
Logger.log(adgroup.getCampaign().getName()+" => "+adgroup.getName()+" => "+adgrouptemplate)
}
}
}
What it does
This script adds two dynamic UTM’s to your tracking template:
CampaignName and AdGroupName
TrackingTemplate
You can edit this variable to add/remove data that will appear in the destination URL the users are sent to.
CAMPAIGN_CONTAINS
This variable can be used to filter in only specific campaigns by their name. For example, if all your brand campaigns have a standard naming convention, e.g. Brand_Jaspers_UK_Exact, you can simply adjust the variable to _CAMPAIGN_CONTAINS = “Brand”.
To apply the script to all campaigns in the account, simply leave this variable empty.
ADGROUP_CONTAINS
Similarly, this variable can be used to filter in only specific ad groups by their name. For example, if all your exact match ad groups have a standard naming convention, e.g. Brand_Jaspers_UK_Exact, you can simply adjust the variable to _ADGROUP_CONTAINS = “Exact”.
To apply the script to all ad groups in the account, simply leave this variable empty.
Important
Make sure you remove all existing tracking templates from your account (on all levels). This script will only run on campaigns that are active.
Installing the script
After making the relevant adjustments to the script, you can go ahead and install it on your account.
Navigate to Tools -> Bulk Actions -> Scripts. Create a new script by clicking the blue plus icon.
Give the script a memorable name, e.g. Auto Campaign and Ad Group UTM’s.
Paste in the script’s code and hit Save.
Google Ads will then ask for your permission to add the script (there’s no fishy code here, pinky swear!). There’s a minor chance it will show an error after confirming the installation, that’s just a bug.
You can then click Run and then Preview.
The code will now run, and its outputs will show in the bottom window. This effectively adds the tracking template to the campaigns and ad groups set in the script (if no filters were set, then to all the account).
Back on the main Scripts page, you can set the frequency at which the script will run (as you want new campaigns/ad groups to use it as well).
The frequency can be set as you see fit. If this account has many changes happening daily, you can set it to update on an hourly basis. This will update both new and existing campaigns (if their name changes).
Final thoughts
This script and post were both contributed by the awesome team at Terrific Digital, which are sailing the high seas of digital marketing with a holistic approach and a bottle of rum.