Be in the know when someone, or something makes changes in your Google Ads account.
Change happens.
Some changes are good. Other changes…not so much.
Occasionally, some of the most terrible changes pop up in your change history.
And the worst thing…they do so without you noticing!
Remember that last client of yours? That stubborn, overly self-confident ego-driven maniac?
He decided he knows best.
Made some “minor” changes to your carefully optimized account.
All by himself.
Changing ad copy, reducing test budgets, and adding non-brand keywords to your BRAND campaign…the horror.
Or maybe that overly ambitious intern that joined your team last week…Negating keywords all over the place!
And let’s not forget the worst of the worse; your colleague accidentally hitting the ‘Apply’ button next to one of Google’s Recommendations.
Oh dear.
Here’s the thing:
It is your job to manage and optmize your account.
Therfore you want be in the know when someone else makes any unanticipated change.
And you want to know immediately!
Thankfully, there’s a script for that!
Today I am sharing a stripped-down version of one of my favorite scripts in my private stack:
Monitoring the change history is, to say the least, a tedious repetitive burden. Not one PPC Pro I know does this on a regular basis.
It will result in Obsessive-Compulsive Disorder if you don’t automate the process.
Now imagine the comfort of being 100% sure that you will be notified immediately when anybody or anything outside of your team makes a change.
After the alert, you can simply jump in and check if the change is in line with your strategy. Or if it will actually hurt your account so you can repair the damage.
That’s exactly what my latest script does.
You can configure a list of users that are anticipated to make changes. This way your sheet and inbox won’t be flooded with alerts for changes that don’t need your review.
The output will look something like this:
(click image to enlarge)
➥ ACTION: Schedule this script to run daily.
(Don’t worry if you have never run a script before. You do not need any coding skills. It is as simple as copy-paste.)
INSTRUCTIONS:
-
- See the script code below. Install the script in your account.
Don’t worry if you have never done this before. You do not need any coding skills. It is as simple as copy-paste. Simply follow these instructions on how to set up and schedule Google Ads scripts. - Create a new Google Sheet
(tip for Chrome users: simply type ‘sheets.new’ in the address bar) - Add the complete URL of the spreadsheet to the script (line 18)
- Add your email address to the script (line 19)
- Add the users for which you do not want to receive alerts (Line 21)
NOTE: If you do not want to receive alerts when Google Ads’ Automated Rules make changes to your account -> simply add the user ‘Bulk Actions’ to the IGNORE_USERS array - Preview
- Schedule to run daily
- See the script code below. Install the script in your account.
/** * * Change History Alerts - stripped down version * * The script checks all the entries in the Google Ads change history of your account, * and if there is a change by a user outside of your list of 'recognized' users, you will get an alert via email. * The alert mail conatins the number of changes as well as a link to the Google Sheet that lists all changes by unrecognized users. * * Version 1.1 * * @author: Nils Rooijmans * * For the more advanced version that runs on both single and manager MCC accounts and allows for Bulk upload and API changes exlusions contact nils@nilsrooijmans.com */ // CHANGE SETTINGS HERE var SPREADSHEET_URL = ""; //insert a new blank spreadsheet url between the double quotes var EMAIL_ADDRESSES = ""; //alert email adresses, insert your email adresses between the double quotes. You can add multiple email addresses between the double quotes, just separate them via a comma, ie: "john@doe.com, jane@doe.com" var IGNORE_USERS = [ 'nils@watercoolertopics.com' ]; // NOTE: if you want to add multiple users, add them between the square brackets, one per line, with a comma seperating the lines. Ie: //var IGNORE_USERS = [ // 'john@doe.com', // 'jane@doe.com', // 'jill@johns.com' // ]; var SEND_EMAIL = true; var EMAIL_SUBJECT = "[GAds Script] - WARNING - Change by person outside of organisation"; var EMAIL_BODY = "\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 "+IGNORE_USERS+" \n"+ " if so, alerts are logged in Google Sheet: "+SPREADSHEET_URL+" \n"+ "\n"+ "If there is an alert an email is sent to:\n"+ EMAIL_ADDRESSES +"\n"; var PERIOD = "LAST_7_DAYS"; function main() { Logger.log("Processing account: "+AdsApp.currentAccount().getName()); var changeAlerts = getChangeAlerts(); if (changeAlerts.length > 0 ) { reportResults(changeAlerts); sendEmail(changeAlerts.length); } else { Logger.log("Could not find any changes in change history that were made by users other than "+IGNORE_USERS+" \n"); } } // look into change history and return changes to add to alert report function getChangeAlerts() { var accountName = AdsApp.currentAccount().getName(); var changes = []; var query = "SELECT " + "campaign.name, " + "ad_group.name, " + "change_event.change_date_time, " + "change_event.change_resource_type, " + "change_event.changed_fields, " + "change_event.client_type, " + "change_event.feed, " + "change_event.feed_item, " + "change_event.new_resource, " + "change_event.old_resource, " + "change_event.resource_change_operation, " + "change_event.resource_name, " + "change_event.user_email " + "FROM change_event " + "WHERE change_event.change_date_time DURING "+PERIOD+" " + "AND change_event.user_email NOT IN ('"+IGNORE_USERS.join("', '")+"') "+ "ORDER BY change_event.change_date_time DESC "+ "LIMIT 9999 "; // max of 10k changes reported per request Logger.log("query: " + query); 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 adGroupName = ""; // hack to prevent undefined variable from stopping script execution try { campaignName = row.campaign.name; adGroupName = row.adGroup.name; } catch(e) { } try { var change = [ row.changeEvent.changeDateTime, accountName, row.changeEvent.userEmail, row.changeEvent.clientType, campaignName, adGroupName, row.changeEvent.changeResourceType, row.changeEvent.changedFields, row.changeEvent.feed, row.changeEvent.feedItem, row.changeEvent.newResource, row.changeEvent.oldResource, row.changeEvent.resourceChangeOperation ]; changes.push(change); } catch(e) { Logger.log("Issue with parsing results from search API: "+e); } } return changes; } function addHeaderToOutputSheet(sheet) { try { var headerSheet = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1HnHwdCTeHGbedV-2gwed36U9YfUB9Bd45NFCFV5R4q4/").getSheetByName('header_sheet'); } catch(e) { console.log(`### There was an issue opening the header sheet. Please download the latest version of this script at https://nilsrooijmans.com\n${e}`); throw `### There was an issue opening the header sheet. Please download the latest version of this script at https://nilsrooijmans.com\n${e}`; } var headerRange = headerSheet.getRange(1, 1, 2, headerSheet.getLastColumn()); var headerData = headerRange.getValues(); console.log("Adding header to the output sheet"); var range=sheet.getRange(1,1,2,headerData[1].length); range.clear(); range.clearFormat(); range.setValues(headerData) range.setFontWeight("bold"); range = sheet.getRange(1,1,1,headerData[1].length); range.setFontColor('#007BFF') sheet.setFrozenRows(2); } function reportResults(changes) { var sheet = prepareOutputSheet(); addOutputToSheet(changes, sheet); } function prepareOutputSheet() { var spreadsheet = SpreadsheetApp.openByUrl(SPREADSHEET_URL); if (!spreadsheet) { alert("Cannot open new reporting spreadsheet") ; return ; } var sheet = spreadsheet.getActiveSheet(); if (!sheet) { alert("Cannot open new reporting sheet") ; return ; } var numberOfRows=sheet.getLastRow() ; // set width of columns sheet.setColumnWidth(1, 150); sheet.setColumnWidth(2, 200); sheet.setColumnWidth(3, 300); sheet.setColumnWidth(4, 300); sheet.setColumnWidth(5, 300); sheet.setColumnWidth(6, 300); sheet.setColumnWidth(7, 100); sheet.setColumnWidth(8, 100); sheet.setColumnWidth(9, 100); sheet.setColumnWidth(10, 100); sheet.setColumnWidth(11, 100); sheet.setColumnWidth(12, 100); sheet.setColumnWidth(13, 100); sheet.setColumnWidth(14, 100); addHeaderToOutputSheet(sheet); return sheet; } function addOutputToSheet(output, sheet) { sheet.insertRowsBefore(3, output.length); // add empty rows below header row var startRow = 3; 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 (SEND_EMAIL) { // send the email var emailBody = "Number of changes: " + numberOfalerts + "\n" + "See details: "+ SPREADSHEET_URL+ "\n" + EMAIL_BODY; MailApp.sendEmail(EMAIL_ADDRESSES, EMAIL_SUBJECT+" | "+accountName, emailBody); Logger.log("Sending alert mail"); } } function alert(string) { Logger.log("### "+string); }
Join thousands of PPC geeks who already have access:
If the button above isn’t working for you, you can sign up here to get access.