Saturday, January 26, 2013

Unique Bidding Rules For Each Campaign

All right folks, today we are going to get a little more complicated.  Let's say you have a set of campaigns that you happen to be managing.  Now, to save time, you want to set up a script to automatically adjust the bids for each campaign based on a set of rules.

The script below will help you do just that.  I am assuming that many of your optimizations are based on cost per conversion as well as the current position on the page. Now we can start to put together a generic script for these optimizations.

The script starts with a CAMP_LIST variable that holds all the campaign names and their rules.  You can add as many campaigns and rules per client as you like.

Take a look and let me know if you have any questions.

Thanks,
Russ

//-----------------------------------
// Unique Bid Updates By Campaign
// Created By: Russ Savage
// FreeAdWordsScripts.com
//-----------------------------------
function main() {
  // this is the structure that holds all the bid information about your accounts.
  var CAMP_LIST = [
    {
      'camp_name' : 'camp name 1',
      'rules' : [
        { 
          'cpv_min' : 0, 'cpv_max' : 10,
          'avg_pos_min' : 2, 'avg_pos_max' : 6,
          'bid_change_amt' : 1.1, 'bid_limit' : 10
        },
        { 
          'cpv_min' : 10, 'cpv_max' : 20,
          'avg_pos_min' : 6, 'avg_pos_max' : 10,
          'bid_change_amt' : 1.2, 'bid_limit' : 10
        }
      ]
    },
    {
      'camp_name' : 'camp name 2',
      'rules' : [
        { 
          'cpv_min' : 0, 'cpv_max' : 5,
          'avg_pos_min' : 3, 'avg_pos_max' : 5,
          'bid_change_amt' : .9, 'bid_limit' : .01
        },
        { 
          'cpv_min' : 5, 'cpv_max' : 20,
          'avg_pos_min' : 5, 'avg_pos_max' : 8,
          'bid_change_amt' : 1.2, 'bid_limit' : 10
        }
      ]
    }
  ];
  var date_range = 'LAST_7_DAYS';
  
  for (index in CAMP_LIST) {
    var camp = CAMP_LIST[index];
    var camp_name = camp.camp_name;
    var rules = camp.rules;
    
    var kw_iter = AdWordsApp.keywords()
      .withCondition("CampaignName CONTAINS_IGNORE_CASE '" + camp_name + "'")
      .get();
    
    while(kw_iter.hasNext()) {
      var kw = kw_iter.next();
      var kw_stats = kw.getStatsFor(date_range);
      var conv = kw_stats.getConversions();
      
      if (conv == 0) { continue; } //skip anything with no conversions
      
      var cost = kw_stats.getCost();
      var cpv = cost/conv;
      var avg_pos = kw_stats.getAveragePosition();
      var max_cpc = kw.getMaxCpc();
      
      for(i in rules) {
        var r = rules[i];
        
        if(cpv >= r.cpv_min && 
           cpv < r.cpv_max && 
           avg_pos >= r.avg_pos_min && 
           avg_pos < r.avg_pos_max) 
        {  
          kw.setMaxCpc(calculate_bid(max_cpc,r.bid_change_amt,r.bid_limit));
          break;
        }
      }
    }
  }
    
  function calculate_bid(current_bid,perc_to_change,max_min_amt) {
    if(perc_to_change >= 1) {
      return (current_bid * perc_to_change > max_min_amt) ? max_min_amt : (current_bid * perc_to_change);
    } else {
      return (current_bid * perc_to_change < max_min_amt) ? max_min_amt : (current_bid * perc_to_change);
    }
  }
}

Wednesday, January 16, 2013

Update Ad Params from Google Spreadsheet

2013-07-11 - NOTE: For those wanting to update AdParams at an AdGroup level, check out an updated version of this script.

Today we have a little script to help adjust keyword ad params at a large scale.  This is an example of how you can use the integration with Google Spreadsheets to update param1 and param2 values for keywords.  I have provided a sample spreadsheet which you can make a copy of.

As long as the Adwords Account and the Google Docs account use the same login, it will be able to access any spreadsheet you have.  I suggest opening my sample spreadsheet and then making a copy if it in your account.  You can then replace the URL from the script below with the url of your copy of the spreadsheet.

Link to my sample spreadsheet: https://docs.google.com/spreadsheet/ccc?key=0Aotb6eheEOpodC1yVjUwc2Y1NDIzUVFLLThJdTFPUnc#gid=0

Thanks,
Russ
/************************************************
* Update Ad Params Using a Google Spreadsheet
* Version 1.1
* ChangeLog v1.1
*  - Added the ability to enable param1 or 2 individually
*  - Looks for Keywords on all sheets
*  - Runs much faster
* Created By: Russ Savage
* FreeAdWordsScripts.com
************************************************/
var SPREADSHEET_URL = "THE URL FOR YOUR GOOGLE SPREADSHEET GOES HERE";
var SET_PARAM1 = true;
var SET_PARAM2 = false;
var DATA_RANGE = 'A:E'; // A - CampaignName, B - AdGroupName, 
                        // C - Keyword, D - Param1, E - Param2

function main() {
  var spreadsheet = SpreadsheetApp.openByUrl(SPREADSHEET_URL);
  var allSheets = spreadsheet.getSheets();
  var allData = [];
  for(var i in allSheets) {
    var sheet = allSheets[i];
    var data = sheet.getRange(DATA_RANGE).getValues();
    data.shift(); //get rid of headers
    allData = allData.concat(data);
  }
  
  var allDataHash = {};
  for(var i in allData) {
    var row = allData[i];
    if(row[0] === '') { continue; }
    var rowKey = [row[0],row[1],row[2]].join('~~!~~');
    allDataHash[rowKey] = { param1 : row[3], param2: row[4] };
  }
  
  var kwIter = AdWordsApp.keywords()
    .withCondition('CampaignStatus = ENABLED')
    .withCondition('AdGroupStatus = ENABLED')
    .withCondition('Status = ENABLED')
    .get();
  
  while(kwIter.hasNext()) { 
    var kw = kwIter.next();
    var campName = kw.getCampaign().getName();
    var adGroupName = kw.getAdGroup().getName();
    var rowKey = [campName,adGroupName,kw.getText()].join('~~!~~');
    if(allDataHash[rowKey]) {
      if(SET_PARAM1) { kw.setAdParam(1, allDataHash[rowKey].param1); }
      if(SET_PARAM2) { kw.setAdParam(2, allDataHash[rowKey].param2); }
    }
  }
}

Tuesday, December 11, 2012

Auto Add ValueTrack Params To All Destination URLs

Today we have a script that will help make sure all of your keywords are tagged with the appropriate ValueTrack parameters.  You can use this to ensure that all of your tracking params are added correctly to your urls.

You can add your own query string parameters to the mapping at the top of the script.  For example, if all the keywords in your account need to have the parameter "channel=sem" in their destination url, you could add that to the URL_PARAMS_TO_ADD mapping at the top of the file and it will be added to all your urls.

Also, I updated the script to allow you to add whatever values you want from the account into the url. See the examples for adding Campaign and AdGroup names to the url if you like.

Thanks,
Russ

/******************************************
* Auto Add ValueTrack (and other) Params If Not There
* Created By: Russ Savage
* Version 1.1
* ChangeLog v1.1
*  - Added the ability to add function calls into the parameters to add
* FreeAdWordsScripts.com
******************************************/
function main() {
   
  var URL_PARAMS_TO_ADD = { 
    "kw" : "{keyword}",
    "crid" : "{creative}",
    "mt" : "{matchtype}",
    "ntwk" : "{network}",
    "ap" : "{adposition}",
    "dv" : "{device}",
    "dvm" : "{devicemodel}",
    //"camp" : getCampaignName,  <----- This function is defined below
    //"ag" : getAdGroupName  <----- and so is this one
  };
   
  var kw_iter = AdWordsApp.keywords()
                  .withCondition("DestinationUrl != ''")
                  .get();
  
  while(kw_iter.hasNext()) {
    var kw = kw_iter.next();
    var destUrl = kw.getDestinationUrl();
    if(!destUrl || destUrl === '') { continue; }
    var toAppend = [];
    for(var key in URL_PARAMS_TO_ADD) {
      if(destUrl.indexOf(key) >= 0) { continue; }
      var value = URL_PARAMS_TO_ADD[key];
      
      //check to see if we should call a function
      if(typeof(value) === 'function') {
        value = encodeURI(value(kw));
      }
      toAppend.push(key+"="+value);
    }
    if(destUrl.indexOf('?') >= 0) {
      kw.setDestinationUrl(destUrl + "&"+toAppend.join('&'));
    } else {
      kw.setDestinationUrl(destUrl + "?"+toAppend.join('&'));
    }
    Logger.log(kw.getDestinationUrl());
  }
}

// As long as the function call is passed a kw object, you can insert
// whatever value you want into the url
function getCampaignName(kw) {
  return kw.getCampaign().getName();
}

function getAdGroupName(kw) {
  return kw.getAdGroup().getName();
}

Wednesday, December 5, 2012

Automating Maintenance Tasks with AdWords Scripting Part 3

In our final installment of automating the monthly maintenance tasks found in AdWords for Dummies with AdWords scripting, let's end with the end of the month clean up of your account.

Declutter your account by shedding keywords that don't have any conversions.


Similar to the previous post, you will need to have set up conversion tracking for all of this to work.  We can actually use a variation of the previous script to accomplish this.  Really we are doing the same thing just increasing bids instead of decreasing them.

//-----------------------------------
// Pause Keywords That Are Not Performing
// Created By: Russ Savage
// FreeAdWordsScripts.com
//-----------------------------------
function main() {
  var THE_VALUE_OF_ONE_CONVERSION = 10;
  var DECREASE_BIDS_BY_PERCENTAGE = .5;
  
  var kw_iter = AdWordsApp.keywords()
    .withCondition("Status = ENABLED")
    .get();
  
  while(kw_iter.hasNext()) {
    var kw = kw_iter.next();
    var kw_stats = kw.getStatsFor("LAST_30_DAYS");
    var cost = kw_stats.getCost();
    var conversions = kw_stats.getConversions();
    if(conversions == 0) {
      if(THE_VALUE_OF_ONE_CONVERSION * 5 > cost) {
        kw.pause();
      }
      else if(THE_VALUE_OF_ONE_CONVERSION * 2 > cost) {
        kw.setMaxCpc(kw.getMaxCpc() * (1-DECREASE_BIDS_BY_PERCENTAGE)); 
      }
    }else{
      //no conversions on this keyword
      //we will deal with that later
      continue;
    }
  }
}

Thanks,
Russ