Resolve (not set) & (organic) as Google Ads campaign names in GA4

Tobias Pennings
September 29, 2024

Are you having problems with (organic) or (not set) values displayed for Google Ads traffic in Google Analytics 4? Are you looking for a quick fix without waiting for an update from Google? In this blog article, we share a guide to solve this problem written by Jan Zdarsa. To find Jan Zdarsa's script, please also refer to his GitHub.

Are you suffering from this problem?

Since June 2024, we, our partner agencies and worldwide other users of Google Analytics 4 noticed an issue with the reporting. The problem takes place in the campaign name in the Acquisition Report. It shows values such as (organic) or (not set) for Google Ads traffic in the campaign names.

To check your GA4 to see if this behavior affects you, follow these steps:

  1. Go to 'Traffic acquisition' report
  2. Change your dimension from 'Default Channel Group' to 'Session Source/Media'
  3. Filter on all visitors from 'google / cpc'
  4. Add 'Session campaign' as a secondary dimension

Do you see (organic) or (not set) values? If so, this problem affects you as well. This behavior affects most inbound traffic received from Google Ads. This is an important problem that you should not ignore if you want to analyze your Google Ads campaigns effectively.

How do you solve this?

Overwrite the destination URLs manually

As you have also read in one of our previous blog articles, it is very important to use UTM parameters in your campaigns to create a clear overview of your traffic in GA4. The same approach applies to Google Ads as well. Only this problem of Google Ads is not only solved by using standard UTM parameters because of visitors who reject 'ad_user_data' in the cookie banner or visitors who have disabled personalized ads in Google's My Ad Center.

Scripts to override UTM Tagging

Let the Google Ads script, written and shared by Jan Zdarsa, do the work for you. It is easy to use and helps you provide your campaigns' URLs with the correct UTM parameters, even if you change the name of your campaign. Set it to run every hour to make sure your destination URLs are always up-to-date. Make sure automatic tagging is enabled. If the system cannot find the campaign name, it will use the UTM value instead of (organic) or (not set) in automatic tagging.

Notice! Google Ads scripts do not support Demand Gen and Video campaigns.

Script: UTM tagging for individual accounts

Here is the script for individual accounts. Check the GitHub account to check if this script is still up to date.

function main() {
 Logger.log("Processing account: " + AdsApp.currentAccount().getCustomerId());
 // Process each campaign type with error handling
 tryHandleCampaigns(AdsApp.campaigns(), "Standard & Others");
 tryHandleCampaigns(AdsApp.shoppingCampaigns(), "Shopping");
 tryHandleCampaigns(AdsApp.performanceMaxCampaigns(), "Performance Max");
 // tryHandleCampaigns(AdsApp.videoCampaigns(), "Video");
 Logger.log("Processing completed for account: " + AdsApp.currentAccount().getCustomerId());
}

function tryHandleCampaigns(campaignIteratorFunction, campaignType) {
 try {
   handleCampaigns(campaignIteratorFunction, campaignType);
 } catch (e) {
   Logger.log("Error processing " + campaignType + " campaigns: " + e.message);
 }
}

function handleCampaigns(campaignIteratorFunction, campaignType) {
 var campaignIterator = campaignIteratorFunction
                           .withCondition('Status != REMOVED')
                           .get();
 while (campaignIterator.hasNext()) {
   var campaign = campaignIterator.next();
   try {
     var trackingTemplate = "{lpurl}?utm_source=google&utm_medium=cpc&utm_campaign={_campaign}&utm_id=" + campaign.getId();
     campaign.urls().setTrackingTemplate(trackingTemplate);
     // Set custom parameters or perform other universal actions here
     campaign.urls().setCustomParameters({campaign: encodeURIComponent(campaign.getName())});
   } catch (e) {
     Logger.log("Error processing campaign " + campaign.getId() + " in " + campaignType + ": " + e.message);
   }
 }
}

Script: UTM tagging for manager account

Do you prefer the manager version of the script that allows you to manage this for multiple accounts simultaneously? Check the GitHub page to check if this script is still up to date.

function main() {
 const accountSelector = AdsManagerApp.accounts().withLimit(50); // Adjust the limit as needed
 accountSelector.executeInParallel('processAccount', 'allFinished');
}

function processAccount() {
 var account = AdsApp.currentAccount();
 Logger.log("Processing account: " + account.getCustomerId());
 // Process each campaign type with error handling
 tryHandleCampaigns(AdsApp.campaigns(), "Standard & Others");
 tryHandleCampaigns(AdsApp.shoppingCampaigns(), "Shopping");
 tryHandleCampaigns(AdsApp.performanceMaxCampaigns(), "Performance Max");
 // tryHandleCampaigns(AdsApp.videoCampaigns(), "Video");
 // Return a result that you can use later in allFinished (optional)
 return account.getCustomerId();
}

function tryHandleCampaigns(campaignIteratorFunction, campaignType) {
 try {
   handleCampaigns(campaignIteratorFunction, campaignType);
 } catch (e) {
   Logger.log("Error processing " + campaignType + " campaigns: " + e.message);
 }
}

function handleCampaigns(campaignIteratorFunction, campaignType) {
 var campaignIterator = campaignIteratorFunction
                           .withCondition('Status != REMOVED')
                           .get();
 while (campaignIterator.hasNext()) {
   var campaign = campaignIterator.next();
   try {
     var trackingTemplate = "{lpurl}?utm_source=google&utm_medium=cpc&utm_campaign={_campaign}&utm_id=" + campaign.getId();
     campaign.urls().setTrackingTemplate(trackingTemplate);
     // Set custom parameters or perform other universal actions here
     campaign.urls().setCustomParameters({campaign: encodeURIComponent(campaign.getName())});
   } catch (e) {
     Logger.log("Error processing campaign " + campaign.getId() + " in " + campaignType + ": " + e.message);
   }
 }
}

// This function is optional but allows you to handle any results after all accounts are processed
function allFinished(results) {
 for (var i = 0; i < results.length; i++) {
   var result = results[i];
   if (result.getStatus() === 'OK') {
     Logger.log("Successfully processed account: " + result.getCustomerId());
   } else {
     Logger.log("Failed to process account: " + result.getCustomerId() + " with error: " + result.getError());
   }
 }
}

How do you add this script to Google Ads?