Impact of unknown search queries

Quick insight into the number of impressions, clicks and more from search terms that are no longer visible in accounts.

Start Now!
Impact of unknown search queries in Google Ads
Impact Get started!

 

Since September 2020, not all search terms are visible in Google Ads anymore, ostensibly for user privacy according to Google. This presents a significant challenge for PPC specialists, because you have to make adjustments based on missing figures. Unfortunately, we cannot solve this problem with scripts either, but the script below does provide insight into the impact of these missing search terms in accounts.

The script below is based on the Search Query Illuminator script from Smarter Ecommerce. You can apply this script at MCC level. This allows you to quickly see, per account, which percentage of the impressions, clicks, costs, conversions and conversion value can be attributed to unknown search terms. Although you cannot optimize better using this, you can use this to substantiate it towards (internal) stakeholders or during an initial analysis of an account.

You will find the results of the script in the log.

Settings

  • DATE_RANGE: Specify over how many days back the script should collect the statistics.
  • ACCOUNT_LABEL: Use account labels to select up to 50 accounts.
  • ADNETWORKTYPE: You can adjust the report on Ad Network. The possibilities are SEARCH, CONTENT (Display), MIXED, YOUTUBE_SEARCH, YOUTUBE_WATCH.
The script
// Copyright 2021. Increase BV. All Rights Reserved. License: MIT
//
// Original script by Smarter Ecommerce GmbH. All rights reserved.
// Link: https://smarter-ecommerce.com/en/ppc-scripts/search-query-illuminator/
// Changes by: Tibbe van Asten
//
// Created: 20-05-2021
// Last update: 03-06-2021
//
// ABOUT THE SCRIPT
// This script gives you insights on the impact of hidden search queries
// in your account. See what percentage of impressions, clicks, cost
// and conversion is generated by unknows search queries.
//
////////////////////////////////////////////////////////////////////

var config = {
  
  DATE_RANGE : last_n_days(30),
  
  // Use accountlabels to select specific account. Leave empty to select all account
  // A script can loopt through 50 account maximum
  ACCOUNT_LABEL : 'QS - 1',
  
  // Optionally you may adjust the Ad Network Type used. The default is "SEARCH". 
  // Other possible options are "CONTENT" (for Display Network), "YOUTUBE_SEARCH", "YOUTUBE_WATCH", "MIXED"
  ADNETWORKTYPE : "SEARCH"
  
}

////////////////////////////////////////////////////////////////////

function main() {
  
  var accountIterator = AdsManagerApp
    .accounts()
    .withLimit(50);
  
  if(config.ACCOUNT_LABEL != ""){
    accountIterator = accountIterator.withCondition("LabelNames CONTAINS '" + config.ACCOUNT_LABEL + "'");
  }

  accountIterator = accountIterator.get();
  while(accountIterator.hasNext()){
    var account = accountIterator.next();
    AdsManagerApp.select(account);
    
    processClientAccount(account);
    
  } // accountIterator
  
  afterProcessAllClientAccounts();
  
} // function main

////////////////////////////////////////////////////////////////////

function last_n_days(n) {
  
	var	from = new Date(), to = new Date();
	to.setUTCDate(from.getUTCDate() - n);
  
	return googleDateRange(from, to);
  
} // function last_n_days()

////////////////////////////////////////////////////////////////////

function googleDateRange(from, to) {
  
	function googleFormat(date) {
    
		var date_array = [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()];
    
		if (date_array[1] < 10) date_array[1] = '0' + date_array[1];
		if (date_array[2] < 10) date_array[2] = '0' + date_array[2];
    
		return date_array.join('');
	}
  
	var inverse = (from > to);
	from = googleFormat(from);
	to = googleFormat(to);
  
	var result = [from, to];
	
  if (inverse) {
		result = [to, from];
	}
  
	return result;
  
} // function googleDateRange()

////////////////////////////////////////////////////////////////////

function processClientAccount(account) {
  
    var aggregatedData = initializeEmptyAggregatedDataObject();  

    // Collect data
    var query1 = "SELECT Clicks, Impressions, Cost, Conversions, ConversionValue " + 
        "FROM ACCOUNT_PERFORMANCE_REPORT " + 
        "WHERE AdNetworkType1 = " + config.ADNETWORKTYPE +   
        " DURING " + config.DATE_RANGE;
    aggregateDataFromTotals(AdsApp.report(query1).rows(), "DATA");

    var query2 = "SELECT Month, Clicks, Impressions, Cost, Conversions, ConversionValue " + 
        "FROM SEARCH_QUERY_PERFORMANCE_REPORT " + 
        "WHERE Query!='' AND AdNetworkType1 = " + config.ADNETWORKTYPE +
        " DURING " + config.DATE_RANGE;
    aggregateDataFromSearchQueries(AdsApp.report(query2).rows(), "DATA");

    Logger.log("Account: " + account.getName());
    Logger.log("Report for Ad Network Type " + config.ADNETWORKTYPE);
    Logger.log("--------------------------------");
    Logger.log("All impressions: " + (aggregatedData["DATA"].totalImpressions) + " | Impressions from unknown search terms: " + (aggregatedData["DATA"].totalImpressions - aggregatedData["DATA"].impressionsFromKnownQueries).toFixed(0) + " => " + (((aggregatedData["DATA"].totalImpressions - aggregatedData["DATA"].impressionsFromKnownQueries) / aggregatedData["DATA"].totalImpressions)*100).toFixed(2) + "%");        
    Logger.log("All clicks: " + (aggregatedData["DATA"].totalClicks) + " | Clicks from unknown search terms: " + (aggregatedData["DATA"].totalClicks - aggregatedData["DATA"].clicksFromKnownQueries).toFixed(0) + " => " + (((aggregatedData["DATA"].totalClicks - aggregatedData["DATA"].clicksFromKnownQueries) / aggregatedData["DATA"].totalClicks)*100).toFixed(2) + "%");        
    Logger.log("All costs: " + (aggregatedData["DATA"].totalCost.toFixed(2)) + " | Cost from unknown search terms: " + (aggregatedData["DATA"].totalCost - aggregatedData["DATA"].costFromKnownQueries).toFixed(2) + " => " + (((aggregatedData["DATA"].totalCost - aggregatedData["DATA"].costFromKnownQueries) / aggregatedData["DATA"].totalCost)*100).toFixed(2) + "%");      
    Logger.log("All conversions: " + (aggregatedData["DATA"].totalConversions.toFixed(2)) + " | Conversions from unknown search terms: " + (aggregatedData["DATA"].totalConversions - aggregatedData["DATA"].conversionsFromKnownQueries).toFixed(2) + " => " + (((aggregatedData["DATA"].totalConversions - aggregatedData["DATA"].conversionsFromKnownQueries) / aggregatedData["DATA"].totalConversions)*100).toFixed(2) + "%");         
    Logger.log("All conversionvalue: " + (aggregatedData["DATA"].totalConversionValue.toFixed(2)) + " | Conversionvalue from unknown search terms: " + (aggregatedData["DATA"].totalConversionValue - aggregatedData["DATA"].conversionValueFromKnownQueries).toFixed(2) + " => " + (((aggregatedData["DATA"].totalConversionValue - aggregatedData["DATA"].conversionValueFromKnownQueries) / aggregatedData["DATA"].totalConversionValue)*100).toFixed(2) + "%");                    
    Logger.log("--------------------------------");  
    Logger.log(" ");

    ////////////////////////////////////////////////////////////////////

    function aggregateDataFromTotals(rowIterator, period){
      while(rowIterator.hasNext()) {
        var row = rowIterator.next();
        aggregatedData[period].totalClicks += parseFloat(row["Clicks"].replace(",",""));
        aggregatedData[period].totalImpressions += parseFloat(row["Impressions"].replace(",",""));
        aggregatedData[period].totalCost += parseFloat(row["Cost"].replace(",",""));     
        aggregatedData[period].totalConversions += parseFloat(row["Conversions"].replace(",",""));   
        aggregatedData[period].totalConversionValue += parseFloat(row["ConversionValue"].replace(",",""));   
      }    
    } // function aggregateDataFromTotals

    ////////////////////////////////////////////////////////////////////

    function aggregateDataFromSearchQueries(rowIterator, period){
      while(rowIterator.hasNext()) {
        var row = rowIterator.next();
        aggregatedData[period].clicksFromKnownQueries += parseFloat(row["Clicks"].replace(",",""));
        aggregatedData[period].impressionsFromKnownQueries += parseFloat(row["Impressions"].replace(",",""));
        aggregatedData[period].costFromKnownQueries += parseFloat((row["Cost"].replace(",","")));
        aggregatedData[period].conversionsFromKnownQueries += parseFloat(row["Conversions"].replace(",",""));      
        aggregatedData[period].conversionValueFromKnownQueries += parseFloat(row["ConversionValue"].replace(",","")); 
      }      
    } // function aggregateDataFromSearchQueries

    ////////////////////////////////////////////////////////////////////

    function initializeEmptyAggregatedDataObject() {
      var aggregatedData = {};
      ["DATA"].forEach(function(period){
        aggregatedData[period] = {
          "totalClicks": 0,
          "clicksFromKnownQueries": 0,
          "totalImpressions": 0,
          "impressionsFromKnownQueries": 0,
          "totalCost": 0,
          "costFromKnownQueries": 0,
          "totalConversions": 0,
          "conversionsFromKnownQueries": 0,
          "totalConversionValue": 0,
          "conversionValueFromKnownQueries": 0  
        };           
      });
      return aggregatedData;
    } // function initializeEmptyAggregatedDataObject()
  
} // function processClientAccount()

////////////////////////////////////////////////////////////////////

function afterProcessAllClientAccounts(){
  
  Logger.log("Thanks for using this script by Tibbe van Asten. Winning!");
  
} // function afterProcessAllClientAccounts
Show whole script!
Loading Comments
The Experts
Tibbe van Asten Head of PPC @ Increase
Nils Rooijmans Water Cooler Topics
Martijn Kraan Freelance PPC Specialist
Bas Baudoin Teamlead SEA @ Happy Leads
How about you? JOIN US!
Sharing Knowledge
Caring

Sharing Knowledge

Adsscripts.com is all about sharing knowledge. In the current market, PPC specialists like to keep their knowledge and experience to themselves. We're convinced that sharing knowledge can ensure that everyone gets better at their work. We want to change this by sharing our knowledge about scripts with everyone.

Do you also want to contribute? We are open to new ideas and feedback on everything you find on Adsscripts.com.

Contact us

Training &
Workshop
Contact us!
Adsscripts Training & Workshop