Helping you build a better website
(and other interesting web stuff)

CSSHTMLJavaScriptjQueryMSSQLMySQLPHPSilvaTechnologiesWordpress
Silva Web Designs - Blog

How to create an Advanced AdWords Budget Script

Why use an AdWords Script to solve the problem with a budget?

So you’ve set up AdWords to gain more traffic to your website but you want to limit how much you spend on your campaigns per day/month/year, well this is exactly what we are going to show you today. We’ve heard a fair few horror stories where incorrect setup have caused businesses to pay high fees to Google AdWords simply because it wasn’t set up correctly. We are going to show you three case scenarios in which scripts can monitor exactly how much you spend and not exceed your budget.

The advanced budget script that we will be showing you today can be used to address several use cases:

1. Enforce Account Budgets

Firstly, budgets in AdWords are set at the campaign level. You can use a shared budget and assign all campaigns to it to get an account-level budget, but then you lose the ability to dedicate more money to higher performing campaigns.

We think shared budgets are useful because many advertisers don’t have time to manage things as granularly as they should. However, for those who want to get the very best results it’s better to manage the budget for each campaign separately.

So the first use case is simple, the script pauses all active campaigns when an account’s cost goes above a certain threshold for the month. You still set campaign level daily budgets but as soon as the total cost for the account gets too high, everything is paused.

2. Maintain Monthly Budgets

It could be argued that simply dividing a monthly budget evenly between all days is a fine way to turn the daily budgets AdWords uses into the monthly budgets that a typical business thinks about. But just like shared budgets are not great because you give up granularity, daily budgets that are the same every day don’t give you the control to spend money when it will lead to the best returns.

A great example of this in which we’ll use today is the month of December, since, with this particular month, we may not want to share the budget equally between every day of the month. Retailers know that the biggest opportunity for moving merchandise will come before Christmas. That being said, you might set higher daily budgets for Dec 1-24 than for Dec 25-31. That creates the risk that you could spend more than intended for the entire month. This script lets advertisers enforce a budget for a date range other than daily so that a campaign can automatically be paused when the cost starts to exceed the maximum allowed for the month, regardless of the daily budget.

The script can also be set to check budgets daily, weekly from Sunday through today, or weekly from Monday through today.

3. Apply Small Budgets To Tests

Let’s say you have a campaign that’s working really well and for which you’ve set a high budget, you might be worried that testing new ads or keywords could lead to accidentally spending a lot of money on something that performs worse than your expectations. Here’s a simple example that illustrates this issue. Let’s say you have a campaign with a £500 budget with all exact match keywords that usually produce a ROAS of about 10. Then you add a pretty generic broad match keyword and before you know it this new keyword has spent 90% of that campaign’s budget and is losing money. It stole the budget from what you knew was going to perform well and now you’re left with a loss for the day.

With this script, you can enforce small budgets down to the keyword or ad level. These experimental items can still go into your existing campaigns so you don’t need to maintain separate campaigns for testing.

The script can check budgets at the keyword, ad, ad group, campaign or account level.

The Script & Script Settings

Awesome, we’ve told you all about the script and the benefits and now you want to implement it right? Okay, great, first we are going to show you the code snippet to run on AdWords but make sure to check the script settings after the code script to ensure you have set it up correctly to your expectations!


DEBUG = 0;
 

function main() {

  Logger.log("");
  
//var SETTINGS = new Object();
  currentSetting = new Object();
  currentSetting.scope = "Account";	
  currentSetting.maxCost = getFloat("1000");
  currentSetting.budgetPeriod = "Monthly";
  currentSetting.labelName = "";
  currentSetting.labelToAdd = "stopped by budget script";
  currentSetting.email = "[email protected]";
  currentSetting.pauseItems = "yes";
  currentSetting.reEnableItems = "yes";
  
  currentSetting.logText = "";
  
  currentSetting.currencyCode = AdWordsApp.currentAccount().getCurrencyCode();
  
  switch(currentSetting.reEnableItems.toLowerCase()) {
    case "no":
      currentSetting.reEnableAtStartOfNewPeriod = 0;
      break;
    case "yes":
      currentSetting.reEnableAtStartOfNewPeriod = 1;
      break;
  }
  
  switch(currentSetting.pauseItems.toLowerCase()) {
    case "no":
      currentSetting.pauseWhenExceeds = 0;
      break;
    case "yes":
      currentSetting.pauseWhenExceeds = 1;
      break;
  }
  
  switch(currentSetting.budgetPeriod) {
	  case "Daily":
	  	//Logger.log("daily");
      currentSetting.dateRange = "TODAY";
	  	break;
	  case "Weekly Sun-Sat":
        //Logger.log("weekly sun");
        currentSetting.dateRange = "THIS_WEEK_SUN_TODAY";
	  	break;
      case "Weekly Mon-Sun":
        //Logger.log("weekly mon");
        currentSetting.dateRange = "THIS_WEEK_MON_TODAY";
	  	break;
	  case "Monthly":
        //Logger.log("monthly"); 
	  	currentSetting.dateRange = "THIS_MONTH";
        break;
  }
  
  
    
  var thenAction = "Pause"; // Alert
  var condition1 = "Cost > " + currentSetting.maxCost;
  var labelCondition = "Status = ENABLED";
  if(currentSetting.labelName) {
    labelCondition = "LabelNames CONTAINS_ANY ['" + currentSetting.labelName + "']";
  }
  if(DEBUG == 1) Logger.log("labelCondition: " + labelCondition);

  currentSetting.enabledCounter = 0;
  currentSetting.deletedCounter = 0;
  currentSetting.pausedCounter = 0;
  currentSetting.enabledList = new Array();
  currentSetting.pausedList = new Array();
  currentSetting.deletedList = new Array();
  
  
  // Set up labels
  var labelToAddText = currentSetting.labelToAdd + " (" + currentSetting.budgetPeriod + ")";
  Logger.log("labelToAddText: " + labelToAddText);
  currentSetting.labelToAdd = labelToAddText;
  if(currentSetting.labelToAdd) {
    createLabel(currentSetting.labelToAdd, "This label is used by an account automation. Its name should not be changed.");
  }
  
  
  if(currentSetting.reEnableAtStartOfNewPeriod) {
    // Check current date, day and time
    currentSetting.thisAccountTime = getTimeInThisAccount();
    var currentHour = currentSetting.thisAccountTime.HH;
    //Logger.log("currentHour: " + currentHour);
    var dayOfWeek = currentSetting.thisAccountTime.dayOfWeek;
    //Logger.log("dayOfWeek: " + dayOfWeek);
    var dd = currentSetting.thisAccountTime.dd;
    //Logger.log("dd: " + dd);
    
    //Re-activate paused items when start of a new period
    var pluralText = "";
    if(currentSetting.budgetPeriod.toLowerCase().indexOf("daily") != -1) {
      if(currentHour == 0) {
        reEnable();
        var emailType = "Notification";
        var body = 'Resetting daily budgets';
        if(currentSetting.enabledList.length > 0) {
          body += "<br/>";
          body += "Re-Enabled Because We Started a New Daily Budget Period:<br/><ul>";
          
          for(var itemCounter = 0; itemCounter < currentSetting.enabledList.length; itemCounter++) {
            var item = currentSetting.enabledList[itemCounter];
            body += "<li>" + item + "</li>";
          }
          body += "</ul>";
        }
        sendEmailNotifications(currentSetting.email, "Daily Budgets Reset", body, emailType );
        if(currentSetting.enabledList.length > 1) pluralText = "s";
        currentSetting.logText += currentSetting.enabledList.length + " " + currentSetting.scope + pluralText + " enabled. ";
      }
    } else if(currentSetting.budgetPeriod.toLowerCase().indexOf("weekly sun-mon") != -1) {
      if(dayOfWeek == 7 && currentHour == 0) {
        reEnable();
        var emailType = "Notification";
        var body = 'Resetting weekly budgets<br/>';
        if(currentSetting.enabledList.length > 0) {
          body += "<br/>";
          body += "Re-Enabled Because We Started a New Weekly Budget Period:<br/><ul>";
          
          for(var itemCounter = 0; itemCounter < currentSetting.enabledList.length; itemCounter++) {
            var item = currentSetting.enabledList[itemCounter];
            body += "<li>" + item + "</li>";;
          }
          body += "</ul>";
        }
        sendEmailNotifications(currentSetting.email, "Weekly Budgets Reset", body, emailType );
        if(currentSetting.enabledList.length > 1) pluralText = "s";
        currentSetting.logText += currentSetting.enabledList.length + " " + currentSetting.scope + pluralText + " enabled. ";
      }
    } else if(currentSetting.budgetPeriod.toLowerCase().indexOf("weekly mon-sun") != -1) {
      if(dayOfWeek == 1 && currentHour == 0) {
        reEnable();
        var emailType = "Notification";
        var body = 'Resetting weekly budgets<br/>';
        if(currentSetting.enabledList.length > 0) {
          body += "<br/>";
          body += "Re-Enabled Because We Started a New Weekly Budget Period:<br/><ul>";
          
          for(var itemCounter = 0; itemCounter < currentSetting.enabledList.length; itemCounter++) {
            var item = currentSetting.enabledList[itemCounter];
            body += "<li>" + item + "</li>";;
          }
          body += "</ul>";
        }
        sendEmailNotifications(currentSetting.email, "Weekly Budgets Reset", body, emailType );
        if(currentSetting.enabledList.length > 1) pluralText = "s";
        currentSetting.logText += currentSetting.enabledList.length + " " + currentSetting.scope + pluralText + " enabled. ";
      }
    } else if(currentSetting.budgetPeriod.toLowerCase().indexOf("monthly") != -1) {
      if(dd == 1 && currentHour == 0) {
        reEnable();
        var emailType = "Notification";
        var body = 'Resetting monthly budgets on the ' + dd + 'st of the month at ' + currentHour + "h<br/>";
        if(currentSetting.enabledList.length > 0) {
          body += "<br/>";
          body += "Re-Enabled Because We Started a New Monthly Budget Period:<br/><ul>";
          
          for(var itemCounter = 0; itemCounter < currentSetting.enabledList.length; itemCounter++) {
            var item = currentSetting.enabledList[itemCounter];
            body += "<li>" + item + "</li>";;
          }
          body += "</ul>";
        }
        sendEmailNotifications(currentSetting.email, "Monthly Budgets Reset", body, emailType );
        if(currentSetting.enabledList.length > 1) pluralText = "s";
        currentSetting.logText += currentSetting.enabledList.length + " " + currentSetting.scope + pluralText + " enabled. ";
      }
    }
  }
  
  // ------------------------------
  // CHECK IF BUDGETS HAVE EXCEEDED
  // ------------------------------
  
  // Account
  if(currentSetting.scope.toLowerCase().indexOf("account") != -1) {
    var fields = "Cost";
    var reportIterator = AdWordsApp.report('SELECT ' + fields +
      ' FROM ACCOUNT_PERFORMANCE_REPORT DURING ' + currentSetting.dateRange).rows();
    
    while(reportIterator.hasNext()) {
    var row = reportIterator.next();
    var cost = getFloat(row["Cost"]).toFixed(2);
      if(DEBUG == 1) Logger.log("Cost: " + cost + " currentSetting.maxCost: " + currentSetting.maxCost);
    }
    if(cost > currentSetting.maxCost) {
      // check if all campaigns are paused
      var campaignIterator = AdWordsApp.campaigns()
      .withCondition("Status = ENABLED")
      .get();
      
      var numActiveCampaigns = campaignIterator.totalNumEntities();
      
      if(numActiveCampaigns > 0) {
        var body = "The total cost for the account '" + AdWordsApp.currentAccount().getName() + "' (" + AdWordsApp.currentAccount().getCustomerId() + ") was " + currentSetting.currencyCode + " " + cost
                   + " as of the time of this email. The maximum allowed cost is " + currentSetting.currencyCode + " " + currentSetting.maxCost.toFixed(2) + " " + currentSetting.budgetPeriod + "." +
                     "<br/><br/>The account will continue to accrue more cost unless you take action like pausing all campaigns.";
        var emailType = "warning";
        sendEmailNotifications(currentSetting.email, "Account Budget Exceeded", body, emailType );
        //Logger.log("email sent");
        currentSetting.logText = "Account cost of " + currentSetting.currencyCode + " " + cost + " exceeds the maximum " + currentSetting.budgetPeriod + " cost of " + currentSetting.currencyCode + " " + currentSetting.maxCost.toFixed(2);
        
        if(currentSetting.pauseWhenExceeds) {
          while (campaignIterator.hasNext()) {
            var campaign = campaignIterator.next();
            var name = campaign.getName();
            campaign.pause();
            campaign.applyLabel(currentSetting.labelToAdd);
            currentSetting.pausedCounter++;
            currentSetting.pausedList.push(name);
          }
        }
      }
    } else {
      Logger.log("Account cost is currently " + currentSetting.currencyCode + cost + " and this does not exceed the allowed budget for the account.");
    }
  }
  
  // Campaigns
  else if(currentSetting.scope.toLowerCase().indexOf("campaign") != -1) {
    
    if(currentSetting.labelName != "") {
      var isLabelUsed = checkIfLabelIsUsed(currentSetting.scope, currentSetting.labelName);
    } 
    
    
    if(DEBUG == 1) Logger.log(condition1);
    if(DEBUG == 1) Logger.log(labelCondition);
    if(DEBUG == 1) Logger.log(currentSetting.dateRange);
    
    // SEARCH AND DISPLAY CAMPAIGNS
    var iterator = AdWordsApp.campaigns()
    .withCondition(condition1)
    .withCondition("Status = ENABLED")
    .withCondition(labelCondition)
    .forDateRange(currentSetting.dateRange)
    .get();
    
    while(iterator.hasNext()){
      var item = iterator.next();
      var name = item.getName();
      var cost = item.getStatsFor(currentSetting.dateRange).getCost();
      item.applyLabel(currentSetting.labelToAdd);
      Logger.log("Match found: '" + name + "' cost: " + currentSetting.currencyCode + " " + cost);
      if(currentSetting.pauseWhenExceeds) item.pause();
      currentSetting.pausedCounter++;
      currentSetting.pausedList.push(name + " cost: " + currentSetting.currencyCode + " " + cost);
    } 
    
    
    
    if(currentSetting.pausedCounter == 0) {
      Logger.log("No campaigns exceeded their allowed budgets for the budget period.");
    } else {
      //Logger.log("something else");
    }
    
    
  } else
  
    
  // Ad Groups
  if(currentSetting.scope.toLowerCase().indexOf("ad group") != -1) {
    
    if(currentSetting.labelName != "") {
      var isLabelUsed = checkIfLabelIsUsed(currentSetting.scope, currentSetting.labelName);
    } 
    
    // SEARCH AND DISPLAY CAMPAIGNS
    var iterator = AdWordsApp.adGroups()
     .withCondition(condition1)
     .withCondition("Status = ENABLED")
     .withCondition(labelCondition)
     .forDateRange(currentSetting.dateRange)
     .get();
    
    while(iterator.hasNext()){
      var item = iterator.next();
      var name = item.getName();
      var cost = item.getStatsFor(currentSetting.dateRange).getCost();
      var campaignName = item.getCampaign().getName();
      Logger.log("Match found: " + "campaign: '" + campaignName + "' ad group: '" + name + "' cost: " + currentSetting.currencyCode + " " + cost);
      item.applyLabel(currentSetting.labelToAdd);
      if(currentSetting.pauseWhenExceeds) item.pause();
      currentSetting.pausedCounter++;
      currentSetting.pausedList.push("campaign: '" + campaignName + "' ad group: '" + name + "'" + " cost: " + currentSetting.currencyCode + " " + cost);
    } 
    
    
    
    if(currentSetting.pausedCounter == 0) {
      Logger.log("No ad groups exceeded their allowed budgets for the budget period.");
    }
    
    
    // Ads
  } else if(currentSetting.scope.toLowerCase().indexOf("ad text") != -1) {
    
    if(currentSetting.labelName != "") {
      var isLabelUsed = checkIfLabelIsUsed(currentSetting.scope, currentSetting.labelName);
    } 
    
    var adIterator = AdWordsApp.ads()
     .withCondition(condition1)
     .withCondition("Status = ENABLED")
     .withCondition(labelCondition)
     .forDateRange(currentSetting.dateRange)
     .get();
    
    
    while(adIterator.hasNext()) {
      
      var ad = adIterator.next();
      var adHeadline = ad.getHeadline();
      var description1 = ad.getDescription1();
      var description2 = ad.getDescription2();
      var displayUrl = ad.getDisplayUrl();
      var cost = ad.getStatsFor(currentSetting.dateRange).getCost();
      ad.applyLabel(currentSetting.labelToAdd);
      
      Logger.log("Match found: " + adHeadline + " " + description1 + " " + description2 + " " + displayUrl  + " -- cost: " + currentSetting.currencyCode + " " + cost);
      var fullAdText = adHeadline + " " + description1 + " " + description2 + " " + displayUrl;
      if(currentSetting.pauseWhenExceeds) ad.pause();
      currentSetting.pausedCounter++;
      currentSetting.pausedList.push(fullAdText + " -- cost: " + currentSetting.currencyCode + " " + cost);
    } // while(adIterator.hasNext())
    
    if(currentSetting.pausedCounter == 0) {
      Logger.log("No ads exceeded their allowed budgets for the budget period.");
    }
    
    
    // Keywords
  } else if(currentSetting.scope.toLowerCase().indexOf("keyword") != -1) {
    
    if(currentSetting.labelName != "") {
      var isLabelUsed = checkIfLabelIsUsed(currentSetting.scope, currentSetting.labelName);
    } 
    
    var iterator = AdWordsApp.keywords()
     .withCondition(condition1)
     .withCondition("Status = ENABLED")
     .withCondition(labelCondition)
     .forDateRange(currentSetting.dateRange)
     .get();
    
    while(iterator.hasNext()){
      var item = iterator.next();
      var name = item.getText();
      var cost = item.getStatsFor(currentSetting.dateRange).getCost();
      var campaignName = item.getCampaign().getName();
      var adGroupName = item.getAdGroup().getName();
      Logger.log("Match found: " + "campaign: '" + campaignName + "' ad group: '" + adGroupName + "' kw: '" + name + "' cost: " + currentSetting.currencyCode + " " + cost);
      item.applyLabel(currentSetting.labelToAdd);
      if(currentSetting.pauseWhenExceeds) item.pause();
      currentSetting.pausedList.push("campaign: '" + campaignName + "' ad group: '" + adGroupName + "' kw: '" + name + "'" + " cost: " + currentSetting.currencyCode + " " + cost);
      currentSetting.pausedCounter++;
    } // while iterator hasnext
    
    if(currentSetting.pausedCounter == 0) {
      Logger.log("No keywords exceeded their allowed budgets for the budget period.");
    }
    
    
  } // if scope == keyword
  
  
  currentSetting.pausedList.sort();
  if(currentSetting.pausedCounter > 1) {
    var pluralText = "s";
  } else {
    var pluralText = "";
  }
  var body = currentSetting.pausedCounter + " " + currentSetting.scope + pluralText + " exceeded the " + currentSetting.budgetPeriod + " maximum cost of " + currentSetting.currencyCode + " " + currentSetting.maxCost.toFixed(2) + ". ";
  if(currentSetting.pauseWhenExceeds) body += "They were paused by the script.";
  body += "<br/><ul>";
  
  if(DEBUG == 1) Logger.log("currentSetting.email: " + currentSetting.email + " currentSetting.pausedCounter: " + currentSetting.pausedCounter);
  if(currentSetting.email && currentSetting.pausedCounter > 0) {
    
    var changesMadeOrSuggestedText = "suggested";
    if(currentSetting.pauseWhenExceeds) changesMadeOrSuggestedText = "made";
    
    var pausedOrSuggestedText = "exceeded budget";
    if(currentSetting.pauseWhenExceeds) pausedOrSuggestedText = "paused";
    
    var subject = "Automated Rules for " + AdWordsApp.currentAccount().getName() + ": " + currentSetting.pausedCounter + " change" + pluralText + " " + changesMadeOrSuggestedText;
    currentSetting.logText += currentSetting.pausedCounter + " " + currentSetting.scope + pluralText + " " + pausedOrSuggestedText;
    for(var itemCounter = 0; itemCounter < currentSetting.pausedList.length; itemCounter++) {
      var item = currentSetting.pausedList[itemCounter];
      body += "<li>" + item + "</li>";
    }
    body += "</ul><br/>";
    body += "These items were labeled '" + currentSetting.labelToAdd + "' for easy identification.<br/><br/>";
    body += "Thanks for using a free Optmyzr.com script. Try our Enhanced Scripts for AdWords which have several benefits:<ul><li>automatically updated when AdWords changes</li><li>works with MCC or individual accounts</li><li>change settings without touching a single line of code</li></ul>";
    body += "Get a 2 week free trial at <a href='https://www.optmyzr.com?utm_campaign=free_scripts'>optmyzr.com</a>";
    
    
    var emailType = "notification";
    if(DEBUG == 1) Logger.log("sending email...");
    sendEmailNotifications(currentSetting.email, subject, body, emailType )
  }
}

  

  
  function reEnable(){
  // Campaigns or Account
  if(currentSetting.scope.toLowerCase().indexOf("campaign") != -1 || currentSetting.scope.toLowerCase().indexOf("account") != -1) {
    var iterator = AdWordsApp.campaigns()
     .withCondition("LabelNames CONTAINS_ANY ['" + currentSetting.labelToAdd + "']")
     .get();
    
    while(iterator.hasNext()){
      var item = iterator.next();
      var name = item.getName();
      item.removeLabel(currentSetting.labelToAdd)
      Logger.log("Enabling campaign: " + name);
      item.enable();
      currentSetting.enabledCounter++;
      currentSetting.enabledList.push(name); 
    } 
    
    // SHOPPING CAMPAIGNS
    var iterator = AdWordsApp.shoppingCampaigns()
     .withCondition("LabelNames CONTAINS_ANY ['" + currentSetting.labelToAdd + "']")
     .get();
    
    while(iterator.hasNext()){
      var item = iterator.next();
      var name = item.getName();
      item.removeLabel(currentSetting.labelToAdd)
      Logger.log("Enabling campaign: " + name);
      item.enable();
      currentSetting.enabledCounter++;
      currentSetting.enabledList.push(name); 
    } 
    
  } else
  
    
  // Ad Groups
  if(currentSetting.scope.toLowerCase().indexOf("ad group") != -1) {

    var iterator = AdWordsApp.adGroups()
    .withCondition("LabelNames CONTAINS_ANY ['" + currentSetting.labelToAdd + "']")
    .get();
    
    while(iterator.hasNext()){
      var item = iterator.next();
      var name = item.getName();
      var campaignName = item.getCampaign().getName();
      Logger.log("Enabling campaign: '" + campaignName + "' ad group: '" + name + "'");
      item.removeLabel(currentSetting.labelToAdd);
      
      item.enable();
      currentSetting.enabledCounter++;
      currentSetting.enabledList.push("campaign: '" + campaignName + "' ad group: '" + name + "'");
    }
    
    // SHOPPING AD GROUPS
    var iterator = AdWordsApp.shoppingAdGroups()
    .withCondition("LabelNames CONTAINS_ANY ['" + currentSetting.labelToAdd + "']")
    .get();
    
    while(iterator.hasNext()){
      var item = iterator.next();
      var name = item.getName();
      var campaignName = item.getCampaign().getName();
      Logger.log("Enabling campaign: '" + campaignName + "' ad group: '" + name + "'");
      item.removeLabel(currentSetting.labelToAdd);
      
      item.enable();
      currentSetting.enabledCounter++;
      currentSetting.enabledList.push("campaign: '" + campaignName + "' ad group: '" + name + "'");
    }
    
    
    
    
    // Ads
  } else if(currentSetting.scope.toLowerCase().indexOf("ad text") != -1) {
    
    var adIterator = AdWordsApp.ads()
    .withCondition("LabelNames CONTAINS_ANY ['" + currentSetting.labelToAdd + "']")
    .get();
    
    
    while(adIterator.hasNext()) {
      
      var ad = adIterator.next();
      var adHeadline = ad.getHeadline();
      var description1 = ad.getDescription1();
      var description2 = ad.getDescription2();
      var displayUrl = ad.getDisplayUrl();
      ad.removeLabel(currentSetting.labelToAdd);
      
      Logger.log("Enabling ad: " + adHeadline + " " + description1 + " " + description2 + " " + displayUrl);
      var fullAdText = adHeadline + " " + description1 + " " + description2 + " " + displayUrl;
      
      
      ad.enable();
      currentSetting.enabledCounter++;
      currentSetting.enabledList.push(fullAdText);
      
    } // while(adIterator.hasNext())
    
    
    
    // Keywords
  } else if(currentSetting.scope.toLowerCase().indexOf("keyword") != -1) {
    
    var iterator = AdWordsApp.keywords()
    .withCondition("LabelNames CONTAINS_ANY ['" + currentSetting.labelToAdd + "']")
    .get();
     
    
    while(iterator.hasNext()){
      var item = iterator.next();
      var name = item.getText();
      var campaignName = item.getCampaign().getName();
      var adGroupName = item.getAdGroup().getName();
      Logger.log("Enabling campaign: '" + campaignName + "' ad group: '" + adGroupName + "' kw: '" + name + "'");
      item.removeLabel(currentSetting.labelToAdd);
      
      
      item.enable();
      currentSetting.enabledCounter++;
      currentSetting.enabledList.push("campaign: '" + campaignName + "' ad group: '" + adGroupName + "' kw: '" + name + "'");
      
    } // while iterator hasnext
  } // if scope == keyword
}

/* getTimeInThisAccount
// ----------------------
// Deals with getting the current time and date in this account
// using the timezone settings of the account.

// returns all values in currentSetting.thisAccountTime object
*/
function getTimeInThisAccount() {
  var weekday = new Array(7);
  weekday[0]=  "Sunday";
  weekday[1] = "Monday";
  weekday[2] = "Tuesday";
  weekday[3] = "Wednesday";
  weekday[4] = "Thursday";
  weekday[5] = "Friday";
  weekday[6] = "Saturday";
  
  var timeZone = AdWordsApp.currentAccount().getTimeZone();
  //Logger.log("time zone: " + timeZone);
  var date = new Date();
  
  var thisAccountTime = new Object();
  thisAccountTime.dayOfWeek = parseInt(Utilities.formatDate(date, timeZone, "uu"));
  thisAccountTime.dd =parseInt(Utilities.formatDate(date, timeZone, "dd"));
  thisAccountTime.weekday = weekday[thisAccountTime.dayOfWeek];
  thisAccountTime.HH = parseInt(Utilities.formatDate(date, timeZone, "HH"));
  thisAccountTime.timeZone = timeZone;
  
  
  return(thisAccountTime);
}

/* createLabel(name, description, backgroundColor)
// ------------
// Makes sure the label doesn't already exists before creating it
// 
*/
function createLabel(name, description, backgroundColor) {
    
    var labelIterator = AdWordsApp.labels()
     .withCondition("Name CONTAINS '" + name + "'")
     .get();
    
    if(labelIterator.hasNext()) {
      Logger.log("Label already exists");
    } else {
      Logger.log("Label needs to be created: " + name + " desc: " + description + " color: " + backgroundColor);
      if(description && backgroundColor) {
        AdWordsApp.createLabel(name, description, backgroundColor);
        Logger.log("Label created");
      } else if (description) {
        AdWordsApp.createLabel(name, description);
        Logger.log("Label created");
      } else {
        AdWordsApp.createLabel(name);
        Logger.log("Label created");
      }
    }
  }	  
  /* checkIfLabelIsUsed(scope, labelName)
// --------------------------------------
// Check is a label that will be used to search for entities is actually used by at
// least 1 of those entities.
//
// This prevents weird cases where the script fails without error due to a missing label
// 
*/
  function checkIfLabelIsUsed(scope, labelName) {
    var entitiesWithLabel = 0;
    var labelIterator = AdWordsApp.labels()
    .withCondition('Name = "' + labelName + '"')
    .get();
    if (labelIterator.hasNext()) {
      var label = labelIterator.next();
      if(scope.toLowerCase().indexOf("campaign") != -1) entitiesWithLabel = label.campaigns().get().totalNumEntities();
      if(scope.toLowerCase().indexOf("ad group") != -1) entitiesWithLabel = label.adGroups().get().totalNumEntities();
      if(scope.toLowerCase().indexOf("ad text") != -1) entitiesWithLabel = label.ads().get().totalNumEntities();
      if(scope.toLowerCase().indexOf("keyword") != -1) entitiesWithLabel = label.keywords().get().totalNumEntities();
      return(entitiesWithLabel);
    }
    
    if(!entitiesWithLabel) {
      Logger.log("No campaigns use the label '" + currentSetting.labelName + "' so this script won't do anything. Update your settings on optmyzr.com with the name of a label that is used for at least 1 campaign.");
    } 
  }
  
  /*
  // emailType can be: notification or warning
  */
  function sendEmailNotifications(emailAddresses, subject, body, emailType ) {
	
    if(emailType.toLowerCase().indexOf("warning") != -1) {
      var finalSubject = "[Warning] " + subject + " - " + AdWordsApp.currentAccount().getName() + " (" + AdWordsApp.currentAccount().getCustomerId() + ")"
    } else if(emailType.toLowerCase().indexOf("notification") != -1) {
      var finalSubject = "[Notification] " + subject + " - " + AdWordsApp.currentAccount().getName() + " (" + AdWordsApp.currentAccount().getCustomerId() + ")"
    }
    
    if(AdWordsApp.getExecutionInfo().isPreview()) {
      var finalBody = "<b>This script ran in preview mode. No changes were made to your account.</b><br/>" + body;
    } else {
      var finalBody = body;
    }
    
	MailApp.sendEmail({
        to:emailAddresses, 
        subject:  finalSubject,
        htmlBody: finalBody
      });
    
    if(DEBUG == 1) Logger.log("email sent to " + emailAddresses + ": " + finalSubject);

  }
  
  function getFloat (input) {
    if(!input || input == "" || typeof(input) === 'undefined') var input = "0.0";
    input = input.toString();
    var output = parseFloat(input.replace(/,/g, ""));
    return output;
  }

Wooooah, that’s a crazy long script, right? Fortunately there are only a few parameters at the top in which you have to tailor to your specific needs.

Okay, so first things first, install the above code to an individual AdWords account (NOT an MCC account).

Here are the lines you’ll need to update in the script to make it work:


<strong>currentSetting.scope = "Account";</strong>

Enter a value of either: Account, Campaign, Ad Group, Keyword, or Ad
This is the level at which the maximum budget will be enforced.


<strong>currentSetting.maxCost = parseFloat("1");</strong>

Enter a decimal value that represents the maximum cost each item is allowed to have.


currentSetting.budgetPeriod = "Daily";

Enter a value of either: Daily, Monthly, Weekly Sun-Sat, Weekly Mon-Sun
This is the time frame for the budget, i.e. the period during which the maximum cost can be accrued.


currentSetting.labelName = "Label name to check";

Enter the name of the label that you’ve added to the items you want the script to check. If you want to check all items of your selected scope (e.g. all keywords), then leave this blank.


currentSetting.labelToAdd = "stopped by budget script";

Enter the name of the label you want the script to add to all items that exceed the allowed budget. This will make it easy for you to find these items in an account, and it is also needed for the script to know what should be re-enabled at the start of a new period.


currentSetting.email = "[email protected]";

Enter the email address of the person to notify whenever a budget has been exceeded or whenever the script makes any changes to the account.


currentSetting.pauseItems = "yes";

Enter a value of either: yes, no
This says if the script should pause items that exceeded the budget (yes) or not (no).


currentSetting.reEnableItems = "yes";

Enter a value of either: yes, no
This says if the script should re-enable any items that were paused by the script when a new budget period commences. The script must be set to run hourly for this to work.

Conculusion

This is a fairly simple script, to be honest, but hopefully, it will help you overcome some of the issues related to how AdWords treats budgets. We recently started using Google AdWords and have used the above script to combat our daily/monthly and yearly budgets in which we spent on Google AdWords, we’ve tested it for quite a while now and it seems pretty solid!

As always, we love to help others… and we hope this helps you too!

Let us know in the comments if this has helped you… If you have any pointers for further improvements… awesome! let us know and we’ll update this blog post to help future users.

Nathan da Silva - Profile

Posted by: Nathan da Silva

Nathan is the Founder of Silva Web Designs. He is passionate about web development, website design and basically anything digital related. His main expertise is with WordPress, Magento, Shopify as well of many other frameworks. Whether you need responsive design, SEO, speed optimisation or anything else in the world of digital then get in touch. If you would like to work with Nathan, simply drop him an email at [email protected]

It’s good to share

Join the discussion