Register for one of our upcoming AdWords scripts workshops.

Bid to Position - Manager Account

This is a Manager Account script. For operating on a single account, use the Single Account version of the script.

MCC Bid to Position extends Bid to Position to run for multiple accounts under an MCC account.

Some advertisers want their ads to show up as high as possible in the search results, no matter the cost. Others choose a more modest ad placement that costs less but provides sufficient visibility for their ads nonetheless.

MCC Bid to Position allows you to eventually steer your keywords in a group of accounts into the desired position. The script looks at keywords in a specified group of child accounts under your MCC account, finds the ones most in need of adjustment, and raises or lowers their bids in order to affect ad positioning.

Scheduling

The script uses the last 7 days of statistics. Schedule it to run Weekly.

How it works

The script iterates through accounts under your root MCC that have a specified label applied to them. For each account, the script finds keywords whose average position is too low, and increases their bids. The script then finds all keywords whose Ctr is better than 1% and average position is too high, and decreases their bids. Once the script completes execution, it emails you a summary of the accounts processed, and how many keywords had their bids adjusted.

Parameters

Update the following parameters in the script:

  • TARGET_ACCOUNT_LABEL - Specifies the account-level label that the script uses to select the list of customer accounts.
  • TOLERANCE - Once the keyword's average position is within TOLERANCE of TARGET_AVERAGE_POSITION, its bids are no longer updated. You don't want a keyword's bid to keep changing up and down because its average position is 2.9 vs. 3.1.
  • BID_ADJUSTMENT_COEFFICIENT - Specifies the multiplier to use when adjusting keyword bids. The larger the multiplier, the more aggressive the bid changes.
  • EMAIL_ADDRESS - Specifies the email address to which the summary report will be sent. Leave this field empty to skip sending email.
  • EMAIL_CC_ADDRESS - Allows you to optionally specify a list of email addresses to which a copy of the summary report will be sent.

Setup

  • Create a new script with the source code below.
  • Don't forget to update TARGET_AVERAGE_POSITION, TARGET_ACCOUNT_LABEL, TOLERANCE, and BID_ADJUSTMENT_COEFFICIENT in the code.
  • Ensure that you have created the account level label specified in TARGET_ACCOUNT_LABEL and applied it to relevant accounts.
  • Specify EMAIL_ADDRESS and EMAIL_CC_ADDRESS if you wish to receive a summary report once the script completes execution.
  • Take a careful look at the conditions used to fetch the keywords.
  • Schedule the script for Weekly.

Source code

// Copyright 2015, Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @name MCC Bid To Position
 *
 * @overview The MCC Bid To Position script adjusts your bids and allows you to
 *     steer ads in a group of advertiser accounts under your MCC account into
 *     a desired position in the search results.
 *     See
 * https://developers.google.com/adwords/scripts/docs/solutions/mccapp-bid-to-position
 *     for more details.
 *
 * @author AdWords Scripts Team [adwords-scripts@googlegroups.com]
 *
 * @version 1.0.1
 *
 * @changelog
 * - version 1.0.1
 *   - Refactored to improve readability.
 * - version 1.0
 *   - Released initial version.
 */

// An account-level label that identifies all the accounts you are trying to
// optimize.
var TARGET_ACCOUNT_LABEL = 'High spend account';

// Ad position you are trying to achieve.
var TARGET_AVERAGE_POSITION = 3;

// Once the keywords fall within TOLERANCE of TARGET_AVERAGE_POSITION,
// their bids will no longer be adjusted.
var TOLERANCE = 0.1;

// How much to adjust the bids.
var BID_ADJUSTMENT_COEFFICIENT = 1.05;

// Email address to send the summary report. Leave blank to skip sending emails.
var EMAIL_ADDRESS = 'YOUR_EMAIL_ADDRESS';

// List of email addresses to which summary report will be cc'd. Leave the array
// empty to skip sending email copies.
var EMAIL_CC_ADDRESS = ['YOUR_EMAIL_CC_ADDRESS'];

/**
 * The main method.
 */
function main() {
  var accountSelector = MccApp.accounts();
  if (TARGET_ACCOUNT_LABEL != '') {
    accountSelector.withCondition(
        'Labels CONTAINS \'' + TARGET_ACCOUNT_LABEL + '\'');
  }
  accountSelector.executeInParallel('adjustBids', 'sendEmail');
}

/**
 * Adjusts the bid for a single account.
 *
 * @return {Object} a result object that has details about how many keyword
 *      bids were adjusted.
 */
function adjustBids() {
  var raisedKeywordCount = raiseKeywordBids();
  var loweredKeywordCount = lowerKeywordBids();
  var retval = {'raised': raisedKeywordCount, 'lowered': loweredKeywordCount};
  return JSON.stringify(retval);
}

/**
 * Raises the bids for keywords in an account.
 *
 * @return {number} The number of keywords whose bids were raised.
 */
function raiseKeywordBids() {
  var keywordsToRaise = getKeywordsToRaise();

  while (keywordsToRaise.hasNext()) {
    var keyword = keywordsToRaise.next();
    keyword.bidding().setCpc(getIncreasedCpc(keyword.bidding().getCpc()));
  }
  return keywordsToRaise.totalNumEntities();
}

/**
 * Lowers the bids for keywords in an account.
 *
 * @return {number} The number of keywords whose bids were lowered.
 */
function lowerKeywordBids() {
  var keywordsToLower = getKeywordsToLower();

  while (keywordsToLower.hasNext()) {
    var keyword = keywordsToLower.next();
    keyword.bidding().setCpc(getDecreasedCpc(keyword.bidding().getCpc()));
  }
  return keywordsToLower.totalNumEntities();
}

/**
 * Increases a given CPC using the bid adjustment coefficient.
 * @param {number} cpc - the CPC to increase
 * @return {number} the new CPC
 */
function getIncreasedCpc(cpc) {
  return cpc * BID_ADJUSTMENT_COEFFICIENT;
}

/**
 * Decreases a given CPC using the bid adjustment coefficient.
 * @param {number} cpc - the CPC to decrease
 * @return {number} the new CPC
 */
function getDecreasedCpc(cpc) {
  return cpc / BID_ADJUSTMENT_COEFFICIENT;
}

/**
 * Gets an iterator of the keywords that need to have their CPC raised.
 * @return {Iterator} an iterator of the keywords
 */
function getKeywordsToRaise() {
  // Condition to raise bid: Average position is greater (worse) than
  // target + tolerance
  return AdWordsApp.keywords()
      .withCondition('Status = ENABLED')
      .withCondition(
          'AveragePosition > ' + (TARGET_AVERAGE_POSITION + TOLERANCE))
      .orderBy('AveragePosition ASC')
      .forDateRange('LAST_7_DAYS')
      .get();
}

/**
 * Gets an iterator of the keywords that need to have their CPC lowered.
 * @return {Iterator} an iterator of the keywords
 */
function getKeywordsToLower() {
  // Conditions to lower bid: Ctr greater than 1% AND
  // average position less (better) than target - tolerance
  return AdWordsApp.keywords()
      .withCondition('Ctr > 0.01')
      .withCondition(
          'AveragePosition < ' + (TARGET_AVERAGE_POSITION - TOLERANCE))
      .withCondition('Status = ENABLED')
      .orderBy('AveragePosition DESC')
      .forDateRange('LAST_7_DAYS')
      .get();
}

/**
 * Send summary report email to users.
 *
 * @param {Array.<ExecutionResult>} results the ExecutionResult array returned
 *      by executeInParallel method.
 */
function sendEmail(results) {
  var emailBody = [];

  emailBody.push(
      '<html>', '<body>',
      '<table width=800 cellpadding=0 border=0 cellspacing=0>', '<tr>',
      '<td colspan=2 align=right>',
      '<div style=\'font: italic normal 10pt Times New Roman, serif; ' +
          'margin: 0; color: #666; padding-right: 5px;\'>' +
          'Powered by AdWords Scripts</div>',
      '</td>', '</tr>', '<tr bgcolor=\'#3c78d8\'>', '<td width=500>',
      '<div style=\'font: normal 18pt verdana, sans-serif; ' +
          'padding: 3px 10px; color: white\'>' +
          'BidToPosition summary report</div>',
      '</td>', '<td align=right>',
      '<div style=\'font: normal 18pt verdana, sans-serif; ' +
          'padding: 3px 10px; color: white\'>',
      AdWordsApp.currentAccount().getCustomerId(), '</h1>', '</td>', '</tr>',
      '</table>', '<table width=800 cellpadding=0 border=0 cellspacing=0>',
      '<tr bgcolor=\'#ddd\'>', '<td style=\'font: 12pt verdana, sans-serif; ' +
          'padding: 5px 0px 5px 5px; background-color: #ddd; ' +
          'text-align: left\'>Customer ID</td>',
      '<td style=\'font: 12pt verdana, sans-serif; ' +
          'padding: 5px 0px 5px 5px; background-color: #ddd; ' +
          'text-align: left\'>Results</td>',
      '</tr>', emailRows(results), '</table>', '</body>', '</html>');


  if (EMAIL_ADDRESS != '') {
    MailApp.sendEmail(
        EMAIL_ADDRESS, 'BidToPosition summary report', '',
        {htmlBody: emailBody.join('\n'), cc: EMAIL_CC_ADDRESS.join(',')});
  }
}

/**
 * Constructs email rows for each customer id processed by the script.
 *
 * @param {Array.<ExecutionResult>} results the ExecutionResult array returned
 *      by executeInParallel method.
 * @return {string} the html body corresponding to the customer ids processed
 *      by the script and its results.
 */
function emailRows(results) {
  var rows = [];
  for (var i = 0; i < results.length; i++) {
    var result = results[i];

    rows.push(
        '<tr>',
        '<td style=\'padding: 5px 10px\'>' + result.getCustomerId() + '</td>',
        '<td style=\'padding: 0px 10px\'>' + getResultDescription(result) +
            '</td>',
        '</tr>');
  }
  return rows.join('\n');
}

/**
 * Gets a description text for execution results on a customer id.
 *
 * @param {ExecutionResult} result the ExecutionResult object returned
 *      by executeInParallel method for the customer id.
 * @return {string} a processed text that summarizes the execution result.
 */
function getResultDescription(result) {
  if (result.getStatus() == 'OK') {
    var retval = JSON.parse(result.getReturnValue());
    return 'Raised : ' + retval.raised + ' < br / > ' +
        'Lowered : ' + retval.lowered;
  } else if (result.getStatus() == 'ERROR') {
    return result.getError();
  } else {
    return 'Script timed out.';
  }
}

Send feedback about...

AdWords Scripts
AdWords Scripts
Need help? Visit our support page.