Dijital Pazarlama 29 Aralık 2021

Google Ads Script – Kampanya Harcama Anomalileri

Eren Dağlar

Google Ads’te kampanyaların harcamaları, akıllı teklif stratejilerilerinden özellikle Hedef ROAS (Target ROAS) ve Hedef EBM(Target CPA) kullanıyorsanız kampanyanın performans trendine göre ani yükselişlere veya azalışlara gebe olabiliyor.  Birden çok hesabınız ve her hesabınızda onlarca kampanya olması durumunda bu anomalileri her gün tespit edebilmeniz imkansız. Bu yazımda, Brainlabs sitesinden bulduğum ve yaklaşık 1.5 senedir kullandığım bir Google Ads Script inden bahsedeceğim.

Benim kullandığım şekilde bana her gece 11’de kampanyaların bugünkü harcamasının, önceki 3 günlük harcama ortalamasının %40 fazlası veya altında ise size bu şartı sağlayan kampanyaları aşağıadaki gibi mail atıyor.

googleads_spend_checker

Kodu istediğiniz şekilde konfigüre edebilirsiniz. İngilizce açıklamaları Türkçeleştirdim.

Aşağıdaki kodda sizin konfigürasyon yapabileceğiniz yerleri kırmızı ile işaretledim. Bunun dışındaki yerlere dokunmayınız. Google Ads hesabınızda Scriptler bölümüne aşağıdaki script’i ekleyebilirsiniz. Script’inizin çalışma saati her gün gece 11 olsun derim. Geç bir saat vermeliyiz ki bugünkü harcamaları yeterince çekebilsin.

 

/**
*
* Change in Campaign Spending Alert
*
*
*
* Version: 1.0
* Google AdWords Script maintained on brainlabsdigital.com
*
**/

function main() {
//Konfigurasyonlar

var campaignNameDoesNotContain = [];
// Kontrol etmesini istemediğiniz kampanyalar için kullanabilirsiniz.
// Örneğin GDN içeren kampanyaları kontrol etmesini istemezseniz “GDN” yazabilirsiniz.,
// Örneğin “GDN”,”PLA” yazarsanız, hem GDN hem de PLA içeren kampanyaları kontrol etmez.
// Tüm kampanyaları kontrol etmesini istiyorsanız olduğu gibi bırakınız.

var campaignNameContains = [];
// Kontrol etmesini istediğiniz kampanyalar için kullanabilirsiniz.
// Örneğin SRCH içeren kampanyaları kontrol etmesini isterseniz “SRCH” yazabilirsiniz,
// Örneğin SRCH ve DSA içeren kampanyaları kontrol etmesini istersenzi “SRCH”,”DSA” yazabilirsiniz.
// Tüm kampanyaları kontrol etmesini istiyorsanız olduğu gibi bırakınız.

var addressesToNotify = [];
// Uyarıları göndermek istediğiniz mail adreslerinizi yazacağınız yerdir:
// [‘[email protected]’, ‘[email protected]’]
// [‘[email protected]’]

//////////////////////////////////////////////////////////////////////////////
// Thresholds

var percentageDifferenceSpend = 40;
// Pozitif değer yazılmalıdır.
// Örnek olarak 40 yazarsanız, kampanyanızın bugünkü harcaması, ortalamaya göre %40 fazla veya az ise mail atar.

var averageSpendMinimumThreshold = 100.00;
// Kampanya harcaması için belirlediğiniz ortalamadır. 100 TL yaparsanız, ortalama harcaması 100 TL ve üzeri olan kampanyaları kontrol eder.

var earliestHour = 22;
// Script’in çalışacağı en erken saati belirlediğiniz yerdir. Script’in gün içinde yeterli datayı toplaması için burayı geç bir saat belirleyiniz.

//////////////////////////////////////////////////////////////////////////////
// Advanced settings

var timePeriod = 3;
// Kampanyaların kaç günlük harcamasının ortalamasını alacağını belirlediğiniz yerdir.

// Örnek olarak 3 yaparsanız, her kampanyanın 3 günlük harcamasının ortalamasını alır ve belirlediğiniz anomali yüzdesine göre mail atar.

//////////////////////////////////////////////////////////////////////////////
// Değiştirmemeniz gereken kodlar aşağıdadır.

// Validate input
var validated = validateInput(
addressesToNotify,
percentageDifferenceSpend,
averageSpendMinimumThreshold,
earliestHour,
timePeriod
);

if (validated !== true) {
throw validated;
}

// Create date strings for AWQL query and data comparison
var dates = makeDates(timePeriod);

// Check if it’s too early to run the script or not
if (dates.currentHour < earliestHour) {
Logger.log(‘Too early for code, need coffee.’);
return;
}

// Get the IDs of the campaigns to look at
var ignorePausedCampaigns = true;
var activeCampaignIds = getCampaignIds(campaignNameDoesNotContain, campaignNameContains, ignorePausedCampaigns);

// Construct the AWQL query using the campaign IDs and dates
var query = constructQuery(activeCampaignIds, dates);

var queryReport = AdWordsApp.report(query);

// Calculate sum of spend today and historically by campaign ID
var costs = calculateCostByCampaign(queryReport, dates);

Logger.log(“Got the costs for all campaigns”);

// Generate a dictionary of overspending campaigns
var overSpendingCampaigns = checkPercentageChange(costs, averageSpendMinimumThreshold, timePeriod, percentageDifferenceSpend);

// Do nothing if there are no overspending campaigns
if (Object.keys(overSpendingCampaigns).length === 0) {
Logger.log(‘No overspending campaigns.’);
return;
}

Logger.log(‘Overspending campaigns: ‘ + JSON.stringify(overSpendingCampaigns));

// Notify contacts if there are overspending campaigns
notifyContact(addressesToNotify, overSpendingCampaigns, averageSpendMinimumThreshold);
Logger.log(“Email sent.”);
}

function validateInput(addressesToNotify,
percentageDifferenceSpend,
averageSpendMinimumThreshold,
earliestHour,
timePeriod
) {

if (addressesToNotify.length === 0) {
return ‘Please provide at least one email address to notify.’;
}

if (percentageDifferenceSpend <= 0) {
return ‘Please provide a positive percentage difference spend.’;
}

if (averageSpendMinimumThreshold <= 0) {
return ‘Please provide a positive average spend minimum threshold.’;
}

if (earliestHour > 23 | earliestHour < 0) {
return ‘Please provide an earliest hour between 0 and 23 inclusive.’
}

if (timePeriod < 1) {
return ‘Please provide a time period of at least one day.’
}

return true;
}

function notifyContact(addresses, overSpendingCampaigns, threshold) {
var accountName = AdWordsApp.currentAccount().getName();
var subject = accountName + ‘ | Spend Checker Script | Campaigns have exceeded your spend change threshold.’;
var body = ‘The following campaigns have exceeded the ‘ + threshold + ‘% spend threshold:\n\n’;

var campaignIds = Object.keys(overSpendingCampaigns);

for (var i = 0; i < campaignIds.length; i++) {
var campaignId = campaignIds[i];
var campaign = overSpendingCampaigns[campaignId];
var campaignName = campaign.campaignName;
var percentageChange = campaign.percentageChange.toFixed(2);
var spendToday = campaign.today.toFixed(2);

body += (i+1) + ‘. \nName: ‘ + campaignName + ‘\n’ +
‘ID: ‘ + campaignId + ‘\n’ +
‘Change(%): ‘ + percentageChange + ‘\n’ +
‘Spend today (TL): ‘ + spendToday + ‘\n\n’;
}

MailApp.sendEmail(addresses.join(‘,’), subject, body);
}

function checkPercentageChange(costs, spendThreshold, timePeriod, percentageThreshold) {
var campaignIds = Object.keys(costs);

return campaignIds.reduce(function(overspendingCampaigns, campaignId){
var campaign = costs[campaignId];
var averageSpend = campaign.sumTimePeriod / timePeriod;
var spendToday = campaign.today;

if(averageSpend < spendThreshold){
return overspendingCampaigns;
}

var percentageChange = ((spendToday – averageSpend) / averageSpend) * 100;

if (Math.abs(percentageChange) > percentageThreshold) {
campaign[‘percentageChange’] = percentageChange;
overspendingCampaigns[campaignId] = campaign;
}

return overspendingCampaigns;
}, {});
}

function makeDates(timePeriod) {
var millisPerDay = 1000 * 60 * 60 * 24;
var timeZone = AdWordsApp.currentAccount().getTimeZone();

var now = new Date();
var dateInPast = new Date(now – ((timePeriod + 1) * millisPerDay));

var todayHyphenated = Utilities.formatDate(now, timeZone, ‘yyyy-MM-dd’);
var todayFormatted = todayHyphenated.replace(/-/g, ”);
var currentHour = Utilities.formatDate(now, timeZone, ‘H’);

var dateInPastFormatted = Utilities.formatDate(dateInPast, timeZone, ‘yyyyMMdd’);

return {
‘todayHyphenated’: todayHyphenated,
‘todayFormatted’: todayFormatted,
‘dateInPastFormatted’: dateInPastFormatted,
‘currentHour’: currentHour,
};

}

function constructQuery(activeCampaignIds, dates) {
var currentHour = dates.currentHour;
var todayFormatted = dates.todayFormatted;
var dateInPastFormatted = dates.dateInPastFormatted;

var query =
‘SELECT CampaignName, CampaignId, Cost, HourOfDay, Date ‘ +
‘FROM CAMPAIGN_PERFORMANCE_REPORT ‘ +
‘WHERE CampaignId IN [‘ + activeCampaignIds.join(‘,’) + ‘] ‘ +
‘AND CampaignStatus = ENABLED ‘ +
‘AND HourOfDay < ‘ + currentHour + ‘ ‘ +
‘DURING ‘ + dateInPastFormatted + ‘,’ + todayFormatted;

Logger.log(‘AWQL Query: ‘ + query);

return query;
}

function calculateCostByCampaign(report, dates) {
var reportRows = report.rows();
var costs = {};

while(reportRows.hasNext()) {
var row = reportRows.next();
var cost = parseFloat(row.Cost);
var campaignId = row.CampaignId;

if (costs[campaignId] === undefined) {
costs[campaignId] = {
‘today’: 0,
‘sumTimePeriod’: 0,
‘campaignName’: row.CampaignName,
}
}

if (row.Date === dates.todayHyphenated) {
costs[campaignId].today += cost;
} else {
costs[campaignId].sumTimePeriod += cost;
}
}

return costs;
}

function getCampaignIds(campaignNameDoesNotContain, campaignNameContains, ignorePausedCampaigns) {
var whereStatement = “WHERE “;
var whereStatementsArray = [];
var campaignIds = [];

if (ignorePausedCampaigns) {
whereStatement += “CampaignStatus = ENABLED “;
} else {
whereStatement += “CampaignStatus IN [‘ENABLED’,’PAUSED’] “;
}

for (var i=0; i<campaignNameDoesNotContain.length; i++) {
whereStatement += “AND CampaignName DOES_NOT_CONTAIN_IGNORE_CASE ‘” + campaignNameDoesNotContain[i].replace(/”/g,’\\\”‘) + “‘ “;
}

if (campaignNameContains.length == 0) {
whereStatementsArray = [whereStatement];
} else {
for (var i=0; i<campaignNameContains.length; i++) {
whereStatementsArray.push(whereStatement + ‘AND CampaignName CONTAINS_IGNORE_CASE “‘ + campaignNameContains[i].replace(/”/g,’\\\”‘) + ‘” ‘);
}
}

for (var i=0; i<whereStatementsArray.length; i++) {
var campaignReport = AdWordsApp.report(
“SELECT CampaignId ” +
“FROM CAMPAIGN_PERFORMANCE_REPORT ” +
whereStatementsArray[i] +
“DURING LAST_30_DAYS”);

var rows = campaignReport.rows();
while (rows.hasNext()) {
var row = rows.next();
campaignIds.push(row[‘CampaignId’]);
}
}

if (campaignIds.length == 0) {
throw(“No campaigns found with the given settings.”);
}

Logger.log(campaignIds.length + ” campaigns found”);
return campaignIds;
}

 

Yararlandığım kaynaklar:

Brainlabs

YouTube kanalımıza abone olun;

https://www.youtube.com/c/boosmart

Instagram hesabımızı takip edin;

https://www.instagram.com/boosmartcom/

Telegram kanalımıza katılın;

https://t.me/boosmart

Diğer içeriklerimizi de inceleyin:

https://boosmart.com/tr/blog/