Automate Bidding Based on Top or Absolute Top Impression Rate

Using Google Ads Script to Automate Bidding Based on Top or Absolute Top Impression Rate

03.10.2019.

One of our clients wants to be a market leader and therefore bids very competitively on certain keywords. But at the same time, he wants to stay ROI positive and doesn’t want the bids to skyrocket. To accomplish this, we developed an automatic bid system based on the average ad position, which enabled him to stay at the top positions most of the time. The script was able to increase bids if the ads were, on average, below the target position and decrease bids if the ads were above the target position and the costs went above reasonable CPC.

Last month Google sunset the average position metric. This metric was replaced with top and absolute top metrics instead. 

Search top impression rate

Search top impression rate “Impr. (Top) %” is the percent of your ad impressions that are shown anywhere above the organic search results.

Search absolute top impression rate

Search absolute top impression rate “Impr. (Abs.Top) %” is the percent of your ad impressions that are shown as the very first ad above the organic search results.

Source.

Since the above metrics are not yet available using Google Ads script methods, we still can’t use them when creating the scripts. And that means a lot of hassle for our client. However, we have found a workaround! In the below post, you can find detailed instructions for this same workaround, which will enable you to automate your bidding based on top or absolute top impression share.

It is only a three-step process. 

In the first step, you will learn how to use labels in order to prepare an account for the script. You will use labels to signal to the script which accounts, campaigns, or keywords you want to manipulate with the script. 

During the second step, you will create an automated Google Drive spreadsheet report with the metrics that aren’t currently available from Google Ads.

Lastly, in the third step, you will get to know the script and learn how to tailor it to your needs.

You will only need 20 minutes to read the blog posts and just three to five hours to set up and test the script.

1. Label Google Ads Accounts and Keywords

1.1 If you are using an MCC account, make sure you use the same label on all of the chosen accounts.

Create Google ads Account lable

1.2 Decide which search impression metric you will use for adjusting the CPC bid. You can choose between the absolute top or top impression rate. Once you’ve made your decision, create labels for the keywords. When creating your label, follow this example:

“Search ImpressionRelative Share-YourDescription”

Label example: »Abs. Top1-MyFavouriteKeyword« In this case, we are choosing absolute top as our search impression metric, and we want 100% relative share for the chosen keyword. 

Label example: »Top0.8-MyFavouriteKeyword« For the chosen keyword, we want our ad to be at the top positions 80% of the time. 

Google ads Keyword Lable

1.3 Once you are done with labels, make sure you label every keyword and Account (in case you are using MCC).

The script will adjust the CPC bid only when the chosen keyword has one label. If you label the keyword twice (with absolute top and top impression share rates) or if you don’t label the keyword, the script won’t adjust the CPC bid.

2. Create an Auto-Updated Google Sheets Report

Make sure you are using the same Google account for both Google Ads and Google Drive, and make sure you are using the admin account.

2.1 Log in to Google Drive, and create a new spreadsheet.

2.2 Add the “Google Ads” addon, and link it with your Google Ads account

2.3 Create a new report. Go to Add-ons → Google Ads → Create a new report.

2.4 Select the report time frame. This will determine how much of the history will influence the search impression metric. We advise you take at least one week into account and not more than 30 days. Choose “Search keywords” as the report type, and name your report. 

The most important step is choosing the report columns. For running the script, you will need the following: “Keyword”, “Label”, “Impr. (Abs. Top) %”, and “Impr. (Top) %” columns. Additionally, you can set up filters and, for example, then filter out keywords where you need to adjust the bids. This will make the script faster. 

Check the Schedule report option, and choose how often you want to refresh the report. You can choose between the daily, weekly (specific day), or monthly (first day of the month) options. 

Pro tip: Make sure you schedule the script after the report update so the script adjusts the bids according to the latest data. 

Google ads add-on settings

2.5 Create the report.

3. Automate Bidding Based on Top or Absolute Top Impression Rate Script

The script should be run on the Google Ads Script service. In the first “parameters” section, you can find and adjust all the necessary parameters. The second “function” part represents the logic for the bid adjustment. 

3.1 Start by adjusting the parameters.

  • spreadUrl: Google Drive URL location of the spreadsheet report
  • sheetName: The name of the workbook inside of the Sheets document (example: “keyword report”)
  • absTopLabel: Keyword level label for all keywords to be manipulated based on the absolute top impressions rate (example: “Abs. Top”)
  • topLabel: Keyword level label for all keywords to be manipulated based on the top search impression rate (example: “Top”)
  • labelColumn: The sequence number of the column where the labels are located (e.g., Column L is the fifteenth column.)
  • absoluteTopImprColumn: The sequence number of the column where “Impr. (Abs. Top)%” metrics are located (e.g., Column L is the eleventh column.)
  • topImprColumn: The sequence number of the column that contains the column “Impr. (top)%” (e.g., Column L is the twelfth column.)
  • keywordNameColumn: The column number of the “Keyword” column (e.g., Column B is the second column.)
  • firstKeyword: The line number of the first keyword (example: 14)
  • targetAccountLabel: To use a script at the MCC level, all accounts you wish to use the script on must be set to the same label (example: “serpBid”)
  • tolerance: The value of the deviation from the desired impression share up or down (For example, “0.05” means a 5% tolerance.)
  • bidAdjustmentCoefficient: The percentage of how much we want to change the CPC up or down (For example, “1.05” represents a 5% change.)
  • log: If we want to see changes in the log when testing the script, check true here; otherwise, set as false.

3.2 Script Functions

The script consists of eight functions that we do not change unless we really know what we are doing.

  • Main: This is where it all begins. Check whether the script will run at the MCC level or not. If it is running, then on the basis of the entered parameter, we can search for accounts and run further functions in parallel on all accounts.
  • adjustBids: First, let’s check if any label starts with the entered absTopLabel and topLabel parameters. This is a check to see if we have entered the correct parameters. If the label exists, then it is called Spreadsheet.
  • labelExists: Check that any label starts with a parameter name (absTopLabel and topLabel). If there are labels, the script returns true. If there are no labels, the script returns false.
  • getIncreasedCpc: This returns the increased CPC for the share specified in the bidAdjustmentCoefficient parameter.
  • getDecreasedCpc: This returns the decreased CPC for the share specified in the bidAdjustmentCoefficient parameter.
  • activeSheet: This opens the spreadsheet and starts reading rows with keywords. For each labeled keyword figure, we can adjust the Absolute Top or Top Search Impression parameters and the desired value. It then calls the function to check the difference between the desired and the actual search impression value.
  • checkDifference: This function checks the difference between the desired value and the actual search impression value. If there is a difference greater than the tolerance, it is called the CPC setting function.
  • keywordsAdjust: The function increases or decreases the CPC on the keyword, depending on the difference between the desired and the actual search impression value.

The script was made for the client and the client’s use case. It could also be more generic, especially when it comes to label format and column number determination in parameters.Improvement options: email notification of errors or email change report.

3.3 Navigate to Google ads –> Tools & Settings and choose Scripts and click on the plus button.

Google ads Scripts

3.4 Copy paste the script in the blank field, choose the script name and authorize the script.

Google ads Automate biding script

4. Testing

Set the “log” parameter to true in the script, and run the script in Preview Mode. Under Logs, you will find all possible changes. Before setting up the scheduler for the script, make sure all changes are correct.

Google ads Script preview

That’s it. You are all set up and ready to rock and roll! Please leave us feedback in the comments, and don’t forget to share the posts with your fellow marketers or growth hackers.

The “Automate Bidding Based on Top or Absolute Top Impression Rate” Google ads script

/* 
**** Script name: Adjust keyword max CPC according to the desired SERP position from Google Ads Keyword Report ****
**** Script author: MDA, Marketing Data Analytics, https://marketing-data-analytics.com ****
**** Script date: September, 2019 ****

**** Script can be updated to send error notifications or summary reports to desired email addresses. ****

Software distributed "AS IS", WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND

First start the script in PREVIEW MODE  and set LOG to true to see changes in LOGS!!!

*/

//Change below paramaters as you require

var spreadUrl ="";  //URL location of report spreadsheet.
var sheetName = "keyword report"; // Sheet name.
var absTopLabel = "Abs. Top"; //Starting characters of absolute top impression label. Case sensitive.
var topLabel = "Top"; //Starting characters of top impression label. Case sensitive.
var labelColumn = "15"; // Label column in spreadsheet is O, 15th column in sheet. Column name "Label".
var absoluteTopImprColumn = "11"; // Absolute Top Impression column in spreadsheet is K - 11th column in sheet. Column name "Impr. (Abs. Top) %".
var topImprColumn = "12"; // Top Impression column in spreadsheet is L, 12th column in sheet. Column name "Impr. (Top) %".
var keywordNameColumn = "2"; // Keyword name column in spreadsheet is B- 2nd column in sheet. Column name "Keyword".
var firstKeyword = "14"; // First keyword in spreadsheet is in 14th row.
var targetAccountLabel = ""; // For using this script on MCC level you need to set same label on all accounts that script will look into. If left empty, script will run on one account level. Case sensitive.
var tolerance = 0.05; // Percentage of tolerance. Defalult set to 0.05 (5%).
var bidAdjustmentCoefficient = 1.05; //Percentage for adjusting the bids. Same for Increase or Decrease. Defalult set to 1.05 (5%).
var log = false; // Set to true or false, if you want to see what script is doing in LOGS. Default set to false.
    

	
// DO NOT CHANGE ANYTHING BELOW THIS;

function main() {
  if(targetAccountLabel != ""){
    var accountSelector = AdsManagerApp.accounts();
    accountSelector.withCondition('LabelNames CONTAINS \'' + targetAccountLabel + '\'');
    accountSelector.executeInParallel('adjustBids');
  }
  else{
    adjustBids();
  }
}

function adjustBids(){
  var absTopLabelExists = labelExists(absTopLabel);
  var topLabelExists = labelExists(topLabel);
  if(!absTopLabelExists && !topLabelExists){
    if(log){
      Logger.log("No label in account "+ AdsApp.currentAccount().getName() +" starts with: "+absTopLabel+ " or "+topLabel);
    }
    return;
  }
  else{  
    activeSheet(absTopLabelExists,topLabelExists);
  }
}

function labelExists(labelName){
  var labelIterator = AdsApp.labels()
      .withCondition('Name STARTS_WITH "'+labelName+'"')
      .get();
   if(labelIterator.totalNumEntities() > 0){
    return true;
   }
  return false;
}

function getIncreasedCpc(cpc) {
  return cpc * bidAdjustmentCoefficient;
}

function getDecreasedCpc(cpc) {
  return cpc / bidAdjustmentCoefficient;
}

function activeSheet(absTopLabelExists,topLabelExists){
  var theSSheet = SpreadsheetApp.openByUrl(spreadUrl);
  var sheet = theSSheet.getSheetByName(sheetName);
  sheet.activate();
  var values = sheet.getDataRange().getValues();
  for(n=firstKeyword-1;n<values.length;n++){ 
    var keywordName = values[n][keywordNameColumn-1]; // [row][column] starts with 0
    var labelName = values[n][labelColumn-1]; 
   
    var absoluteTopKeyword = labelName.lastIndexOf(absTopLabel,0);
    var topKeyword = labelName.lastIndexOf(topLabel,0);
    var desiredShare = "";
    var actualShare = "";
    
    if(absoluteTopKeyword == 0 && topKeyword == -1 && absTopLabelExists){
      var tempLabelName = labelName.replace(absTopLabel,"");
      var firstDash = tempLabelName.indexOf("-");
      desiredShare = tempLabelName.substring(0,firstDash);
      actualShare = values[n][absoluteTopImprColumn-1];
      var toleranceUp = actualShare + tolerance;
      var toleranceDown = actualShare - tolerance;
      chceckDifference(desiredShare,toleranceUp,toleranceDown, keywordName,absTopLabel);
    }
    else if(absoluteTopKeyword == -1 && topKeyword == 0 && topLabelExists){
      var tempLabelName = labelName.replace(topLabel,"");
      var firstDash = tempLabelName.indexOf("-");
      desiredShare = tempLabelName.substring(0,firstDash);
      actualShare = values[n][topImprColumn-1];
      var toleranceUp = actualShare + tolerance;
      var toleranceDown = actualShare - tolerance;
      chceckDifference(desiredShare,toleranceUp,toleranceDown, keywordName,topLabel);
    }
    else{
      if(log){
        Logger.log('Issue with label. Label is not defined as expected or does not exist in this account: '+labelName+'. Report row: '+(n+1));
      }
      continue;
    }
  }
}

function chceckDifference(desiredShare,toleranceUp,toleranceDown, keywordName,label){
  var change = "";
  if(desiredShare > toleranceUp){
    change = "Increase";
    keyowrdsAdjust(keywordName, label,change);
  }
  else if(desiredShare < toleranceDown){
    change = "Decrease";
    keyowrdsAdjust(keywordName, label,change);
  }
  else{
    if(log){
      Logger.log('Bid not adjusted. Keyword '+keywordName+': Desired share: '+desiredShare+'; ToleranceUp:'+toleranceUp+'; toleranceDown: '+toleranceDown);
    }
    return;
  }
}

function keyowrdsAdjust(keywordName,label,change){
  var keywords = AdsApp.keywords().withCondition('Status  = ENABLED').withCondition('Text = "'+keywordName+'"').withCondition("LabelNames CONTAINS_ANY ['"+label+"']").get();
  if(keywords.totalNumEntities() > 0){
    while (keywords.hasNext()) {
      var keyword = keywords.next();
      var oldCPC = keyword.bidding().getCpc();
      if(change == "Increase"){
        keyword.bidding().setCpc(getIncreasedCpc(keyword.bidding().getCpc()));
      }
      else if(change == "Decrease"){
        keyword.bidding().setCpc(getDecreasedCpc(keyword.bidding().getCpc()));
      }
      var newCPC = keyword.bidding().getCpc();
      if(log){
        Logger.log('Keyword bidding changed. Old CPC: '+oldCPC+'. New CPC: '+newCPC);
      }
    }
  }
  else {
    if(log){
      Logger.log('Keyword not found. Maybe it is not enabled, does not exist in this account or has no label. Keyword '+keywordName+'. Label:'+label);
    }
    return;
  }
}

Stay connected