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!Quick insight into the number of impressions, clicks and more from search terms that are no longer visible in accounts.
Start Now!
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.
// 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