Change History MCC Script for Agency Directors
ClickTrain October 2024
The script checks all the entries in your accounts’ Google Ads change history installed at the MCC level. If a user changes outside of your list of ‘recognized’ users or your ‘domain’, you will get an alert via email. The alert mail contains the number of changes and a link to the Google Sheet that lists all changes by unrecognized users.
Follow on Github: https://github.com/andrew-Clicktrain/ppc-listenr-script/blob/main/main.js
/**
*
* The Ultimate Change History MCC Script
*
* The script checks all the entries in the Google Ads change history of your accounts and installed at the MCC level,
* and if there is a change by a user outside of your list of 'recognized' users or your 'domain', you will get an alert via email.
* The alert mail contains the number of changes as well as a link to the Google Sheet that lists all changes by unrecognized users.
*
*
* @initalAuthor: Nils Rooijmans
*
* Branched Version 2.0
* @author: Andrew Bloch
*
* For more features like specific user insights and predicted hours visit - clicktrain.com/ppc-tool/
*/
// CHANGE SETTINGS HERE
var spreadsheetId = ""; // Insert the ID of the new blank main spreadsheet between the double quotes
var emailAddresses = "andrew@test.com";
var yourDomain = "test.com";
var ignoreUsers = [
'john@doe.com'
];
// NOTE: if you want to add multiple users, one per line, with a comma separating the lines. I.e.:
// var ignoreUsers = [
// 'john@doe.com',
// 'jane@doe.com',
// 'jill@johns.com'
// ];
var period = "YESTERDAY";
var sendMail = true;
var emailSubject = "PPCListenr - WARNING - Change by person outside of the Agency";
var emailBody =
"\n" +
"***\n" +
"\n" +
"This script checks changes in the 'Change history':\n" +
"\n" +
"For all changes during " + period + "\n" +
" check if there is a change being made by users other than " + ignoreUsers + "\n" +
" if so, alerts are logged in Google Sheet: https://docs.google.com/spreadsheets/d/" + spreadsheetId + "\n" +
"\n" +
"If there is an alert, an email is sent to:\n" + emailAddresses + "\n";
function main() {
prepareOutputSheet(true)
var ids = [];
var accounts = MccApp.accounts().get();
while (accounts.hasNext() && ids.length < 50) {
var account = accounts.next();
var customerId = account.getCustomerId();
ids.push(customerId);
}
MccApp.accounts().withIds(ids).executeInParallel(
'getChangeAlerts')
}
function getChangeAlerts() {
var accountName = AdsApp.currentAccount().getName();
var changeAlerts = [];
var query = "SELECT " +
"campaign.name, " +
"change_event.change_date_time, " +
"asset.type, " +
"change_event.change_resource_type, " +
"change_event.changed_fields, " +
"change_event.user_email " +
"FROM change_event " +
"WHERE change_event.change_date_time DURING " + period + " " +
"AND change_event.user_email NOT REGEXP_MATCH '.*@" + yourDomain + "' " +
"AND change_event.user_email NOT IN ('"+ignoreUsers.join("', '")+"') "+
"AND change_event.client_type IN ('GOOGLE_ADS_RECOMMENDATIONS', 'GOOGLE_ADS_WEB_CLIENT')" +
"ORDER BY change_event.change_date_time DESC " +
"LIMIT 9999 "; // Max of 10k changes reported per request
try {
var result = AdsApp.search(query);
} catch (e) {
alert("Issue retrieving results from search API: " + e);
}
while (result.hasNext()) {
var row = result.next();
var campaignName = "";
var assetType = "";
try {
campaignName = row.campaign.name;
} catch (e) {}
try {
assetType = row.asset.type;
} catch (e) {}
if (!campaignName && assetType){
campaignName = assetType
}
try {
var change = [
row.changeEvent.changeDateTime,
accountName,
row.changeEvent.userEmail,
campaignName,
row.changeEvent.changeResourceType,
row.changeEvent.changedFields,
];
changeAlerts.push(change)
} catch (e) {
Logger.log("Issue with parsing results from search API: " + e);
}
}
if (changeAlerts.length > 0) {
reportResults(changeAlerts);
sendEmail(changeAlerts.length);
}
}
function addHeaderToOutputSheet(sheet) {
var header = [
"Date",
"Account",
"User",
"Location",
"Type",
"Fields",
];
sheet.appendRow(header);
}
function reportResults(changes) {
let sheet = prepareOutputSheet(false);
addOutputToSheet(changes, sheet);
}
function prepareOutputSheet(clear) {
var spreadsheet = SpreadsheetApp.openById(spreadsheetId);
if (!spreadsheet) {
alert("Cannot open new reporting spreadsheet");
return;
}
var sheet = spreadsheet.getActiveSheet();
if (!sheet) {
alert("Cannot open new reporting sheet") ;
return ;
}
if (clear){
sheet.clearContents();
addHeaderToOutputSheet(sheet);
// sheet.insertRowsBefore(2, output.length); // add empty rows below header row
}
return sheet;
}
function addOutputToSheet(output, sheet) {
var numberOfRows = sheet.getLastRow() ;
var startRow = numberOfRows + 1;
var range=sheet.getRange(startRow, 1, output.length, output[0].length) ;
range.setValues(output) ;
Logger.log("\nNumber of rows added to output sheet: "+output.length+"\n\n");
}
function sendEmail(numberOfAlerts) {
var accountName = AdsApp.currentAccount().getName();
if (sendMail) {
var emailString =
"Number of changes: " + numberOfAlerts + "\n" +
"See details: https://docs.google.com/spreadsheets/d/" + spreadsheetId + "\n" + emailBody;
MailApp.sendEmail(emailAddresses, emailSubject + " | " + accountName, emailString);
Logger.log("Sending alert mail");
}
}
function alert(string) {
Logger.log("### " + string);
}
Why Use a Niche PPC Management Agency?
You have to be honest with yourself; having to run PPC campaigns (especially nowadays) is the furthest thing from being simple. It used to be, but not anymore. Gone are the days when you could just set a budget, throw around some keywords, and then watch all the clicks just pour in. These days, PPC advertising feels more like navigating …

Unpacking PPC Management and How It Drives Growth
Digital marketing is a big world, but there’s one acronym every business owner hears sooner or later: PPC. Pay-per-click advertising has become a cornerstone for driving traffic, generating leads, and fostering business growth. Yes, PPC is one of the most effective ways to achieve rapid visibility online, but the key to success lies in strategic management. But here’s the thing: …