
MCC Bid to Impression Share extends Bid to Impression Share to run for multiple accounts under a manager account.
Some advertisers want their ads to show up as often 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 Impression Share allows you to incrementally reach impression share goals for a group of accounts. The script looks at keywords in a specified group of child accounts under your manager account, finds the ones most in need of adjustment, and raises or lowers their bids in order to affect how often they show in search results.
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 manager account that have a specified label applied to them. For each account, the script finds keywords whose impression share is too low, and increases their bids. The script then finds all keywords whose Ctr is better than 1% and impression share 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_IMPRESSION_SHARE
- Specifies the account-level label that the script uses to select the list of customer accounts.TOLERANCE
- Once the keyword's impression share is within TOLERANCE of TARGET_IMPRESSION_SHARE, its bids are no longer updated. You don't want a keyword's bid to keep changing up and down because its impression share is 49 vs 51.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_IMPRESSION_SHARE
,TARGET_ACCOUNT_LABEL
,TOLERANCE
, andBID_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
andEMAIL_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 Impression Share * * @overview The MCC Bid To Impression Share script adjusts your bids and allows * you to steer ads in a group of advertiser accounts under your MCC account * into a desired impression share in the search results. * See * https://developers.google.com/google-ads/scripts/docs/solutions/adsmanagerapp-bid-to-impression-share * for more details. * * @author Google Ads Scripts Team [adwords-scripts@googlegroups.com] * * @version 2.0.0 * * @changelog * - version 2.0.2 * - Fixed old column name, changed Labels to LabelNames. * - version 2.0.1 * - Fixed logic when determining which keywords to raise and lower. * - version 2.0.0 * - Refactored to target impression share rather than position. * - 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 impression share you are trying to achieve, representated as a percentage. var TARGET_IMPRESSION_SHARE = 0.5; // Once the keywords fall within TOLERANCE of TARGET_IMPRESSION_SHARE, // their bids will no longer be adjusted. Represented as a percentage. var TOLERANCE = 0.05; // 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 = AdsManagerApp.accounts(); if (TARGET_ACCOUNT_LABEL != '') { accountSelector.withCondition( 'LabelNames 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 impression share is worse (less) than // target - tolerance return AdsApp.keywords() .withCondition('Status = ENABLED') .withCondition( 'SearchImpressionShare < ' + (TARGET_IMPRESSION_SHARE - TOLERANCE)) .orderBy('SearchImpressionShare 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 impression share better (greater) than target + tolerance return AdsApp.keywords() .withCondition('Ctr > 0.01') .withCondition( 'SearchImpressionShare > ' + (TARGET_IMPRESSION_SHARE + TOLERANCE)) .withCondition('Status = ENABLED') .orderBy('SearchImpressionShare 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 Google Ads Scripts</div>', '</td>', '</tr>', '<tr bgcolor=\'#3c78d8\'>', '<td width=500>', '<div style=\'font: normal 18pt verdana, sans-serif; ' + 'padding: 3px 10px; color: white\'>' + 'BidToImpressionShare summary report</div>', '</td>', '<td align=right>', '<div style=\'font: normal 18pt verdana, sans-serif; ' + 'padding: 3px 10px; color: white\'>', AdsApp.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, 'BidToImpressionShare 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.'; } }