Jeśli prowadzisz kampanie dla różnych klientów zapewne nie jest Ci obca sytuacja, że na koncie zostały wprowadzone zmiany, o których nikt Cię nie poinformował. W praktyce może to prowadzić do sytuacji, że 2 osoby zmieniają tą samą kampanie i finalnie jej efekty się pogarszają. Niestety Google domyślnie nie przesyła komunikatu o takiej akcji, więc ciężko jest to regularnie śledzić. Z pomocą przychodzi skrypt Google Ads przygotowany przez Nilsa Rooijmansa. Szczegóły poniżej.

Poniżej znajdziesz skrypt, którego autorem jest Nils Rooijmans.
Na początek przejdź na drive.google.com i utwórz nowy Arkusz Google:

Następnie określ nazwę Arkusza i kliknij UDOSTĘPNIJ:

Wybierz opcję KAŻDA OSOBA MAJĄCA LINK i EDYTUJĄCY:

Na końcu skopiuj link – będzie potrzebny w jednym z dalszych kroków.
Zaloguj się na swoje konto Google Ads i przejdź do zakładki SKRYPTY (NARZĘDZIA – DZIAŁANIA ZBIORCZE) i kliknij niebieską ikonkę z plusem wybierając + NOWY SKRYPT:

W oknie do wpisania kodu znajdziesz fragment:
function main() {
}Skasuj go i wklej poniższy kod:
//**
*
* 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.2
*
* @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 = "YESTERDAY";
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);
}
W wierszu 18 między cudzysłowami dodaj adres Arkusza Google (skopiowany we wcześniejszym kroku).
W wierszu 19 między cudzysłowami dodaj swój adres mailowy, na który będą przychodzić powiadomienia mailowe.
W wierszu 21 wpisz adres lub adresy mailowe, które mają być ignorowane w raportowaniu historii zmian, a więc będzie to Twój mail, z którego zarządzasz kontem Google Ads. Fragment skryptu powinien mieć następującą formę:
var IGNORE_USERS = [ 'adresmailowy@gmail.com' ];
Jeśli masz kilka adresów dodaj je w osobnych wierszach:
var IGNORE_USERS = [ 'adresmailowy@gmail.com', 'adresmailowy2@gmail.com' ];
Gdy skończysz dostosowywać skrypt, kliknij AUTORYZUJ i zezwól systemowi na wprowadzanie zmian na koncie.

Następnie kliknij PODGLĄD, aby sprawdzić, czy skrypt poprawnie działa. Po zakończeniu działania skryptu i wykryciu zmian na koncie otrzymasz maila, który będzie wyglądać następująco:

Z kolei Arkusz Google będzie zawierać szczegółowe informacje dotyczące zmian wprowadzony w dniu wczorajszym:

Jeśli skrypt działa poprawnie, zapisz go i ustaw harmonogram jego uruchamiania. Skrypt analizuje historię danych za poprzedni dzień, więc harmonogram warto ustawić raz dziennie w godzinach nocnych, aby już rana dostać maila o ewentualnych zmian z wczoraj.

Dzięki temu możesz regularnie sprawdzać, czy ktoś dokonywał zmian na koncie.
| WSPÓŁPRACA ZE MNĄ |
| Od 2011 roku jako specjalista Google Ads zajmuję się prowadzeniem i optymalizacją kampanii w systemie reklamowym Google. Dotychczas przeprowadziłem ponad 2300 kampanii, których budżet przekroczył już 30 mln zł. Jeśli szukasz kogoś komu chcesz zlecić prowadzenie swoich kampanii, napisz do mnie. Pracuję tylko z firmami, które poważnie podchodzą do tematu, dlatego zapoznaj się proszę z moimi zasadami współpracy. Jeśli je akceptujesz, wyślij mi wiadomość :) |


4 comments
Artur Łowigus
6 maja 2024 at 10:14
Działa i ułatwi z pewnością weryfikację działań prowadzonych zarówno przez Klientów, jak i innych, podległych członków zespołu. Dziękuję
Marcin Wsół
6 maja 2024 at 13:30
nie ma za co :) dzięki za komentarz!
Patrycjusz
6 listopada 2025 at 12:18
Obecnie skrypt nie działa w podanej formie. Żeby zadziałał ponownie należy poprawić funkcję getChangeAlerts()
Marcin Wsół
7 listopada 2025 at 09:30
Hej, dzięki za info. Zbadam temat i postaram się poprawić :)