Vários proponentes: conta única

Ícone de lances

A otimização dos lances de palavra-chave é uma das atividades mais importantes do gerenciamento de contas do Google Ads. Os lances de palavra-chave têm um impacto direto no posicionamento de um anúncio (a classificação dele na Pesquisa Google), bem como no custo. Muitas vezes, você precisa desenvolver suas próprias estratégias de lances para atingir as metas.

Com o recurso de regras automatizadas do Google Ads, você pode modificar lances de palavras-chave com base em critérios definidos e fazer isso de acordo com uma programação. Se você tem um grande número de regras automatizadas, pode ser difícil gerenciá-las.

O recurso Vários licitantes oferece funcionalidade semelhante à das Regras automatizadas com base em uma planilha. Cada linha de uma planilha equivale a uma regra automatizada inteira. Gerenciar essas regras em uma planilha é mais fácil do que no Google Ads.

Captura de tela da planilha com vários bidders

A planilha acima demonstra uma única regra que faz o seguinte:

  • Procura estatísticas para THIS_WEEK_SUN_TODAY.
  • Encontra todas as palavras-chave na Campanha 1 que receberam mais de 1 impressão e cuja CTR é maior do que 25%.
  • Aumenta seus lances em 10%, sem exceder US$ 1,40.

Como funciona

As seguintes colunas na planilha precisam estar presentes:

  • Ação
  • Argumento
  • Limite da parada

Outras colunas podem ser adicionadas ou removidas conforme necessário. Veja a seguir exemplos válidos de nomes de colunas e valores de células correspondentes:

ColunaValor da célula
CampaignName STARTS_WITH '?'Indonesia_
Conversions >= ?4
Status IN [?]'ENABLED', 'PAUSED'

O símbolo ? no nome da coluna é substituído pelo valor na linha correspondente. Verifique a documentação de KeywordSelector para ver um conjunto completo de colunas compatíveis.

Ação

Ação é um dos seguintes:

  • Multiplicar por: multiplica o lance de palavra-chave pelo Argumento. 1.1 aumenta o lance em 10%. 0.8 diminui em 20%.
  • Adicionar: adiciona o Argumento ao lance de palavra-chave. 0.3 aumenta o lance em US $0,30 (supondo que a conta esteja em USD). -0.14 diminui em US$ 0,14.
  • Definir como CPC de primeira página: define o lance de palavra-chave como o CPC de primeira página. Argumento é ignorado.
  • Definir como CPC na parte superior da página: define o lance de palavra-chave como o CPC na parte superior da página. Argumento é ignorado.

Limite da parada

Limite de parada é usado para limitar as alterações no lance que o script faz. Se as alterações no lance forem positivas, ele não ficará maior do que o limite de parada. Se a alteração for negativa, o lance não ficará menor do que o limite de parada. Por exemplo, se no momento o lance de palavra-chave é de R $2,00, e você o aumenta em 25% com um limite de parada de R $3,00, o lance se torna R $2,50. No entanto, se o limite de parada for definido como US $2,30, o lance também se tornará US $2,30.

Resultados

Os resultados são gerados automaticamente pelo script. Ele contém todos os erros que uma execução encontrou ou uma indicação do número de palavras-chave que o script buscou e tentou mudar.

Uma "tentativa de alteração" pode não significar que a alteração real ocorreu, por exemplo, atribuir um lance negativo a uma palavra-chave não funcionará. Verifique os registros de execução para ver exatamente o que o script fez.

A coluna Resultados é a última coluna lida na planilha. Se você adicionar alguma condição depois da coluna Resultados, ela não será aplicada.

Agendamento

As opções de programação mais usadas para regras de lances são Diariamente e Semanalmente.

Saiba como a frequência da programação pode afetar os períodos de estatísticas que você está usando. Como as estatísticas do Google Ads podem atrasar até três horas, evite agendar seu script A cada hora.

Também não é possível programar o script se isso fizer mais sentido.

Instalação

  • Configure um script com base em planilha usando o código-fonte abaixo. Use a planilha modelo de vários bidders.
  • Não se esqueça de atualizar SPREADSHEET_URL no código.
  • Visualize o script antes de executá-lo.
  • Considere se é necessário programar o script.

Código-fonte

// 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 Multi Bidder
 *
 * @overview The Multi Bidder script offers functionality similar to that of
 *     Automated Rules based on a spreadsheet. See
 *     https://developers.google.com/google-ads/scripts/docs/solutions/multi-bidder
 *     for more details.
 *
 * @author Google Ads Scripts Team [adwords-scripts@googlegroups.com]
 *
 * @version 2.0
 *
 * @changelog
 * - version 2.0
 *   - Updated to use new Google Ads scripts features.
 * - version 1.0.3
 *   - Replaced deprecated keyword.getMaxCpc() and keyword.setMaxCpc().
 * - version 1.0.2
 *   - Added validation of user settings.
 * - version 1.0.1
 *   - Improvements to time zone handling.
 * - version 1.0
 *   - Released initial version.
 */

const SPREADSHEET_URL = 'YOUR_SPREADSHEET_URL';

const spreadsheetAccess = new SpreadsheetAccess(SPREADSHEET_URL, 'Rules');

let totalColumns;

/**
 * The bids of keywords are modified based on the criterion mentioned
 * in the spreadsheet.
 */
function main() {
  // Make sure the spreadsheet is using the account's timezone.
  spreadsheetAccess.spreadsheet.setSpreadsheetTimeZone(
      AdsApp.currentAccount().getTimeZone());
  spreadsheetAccess.spreadsheet.getRangeByName('account_id').setValue(
      AdsApp.currentAccount().getCustomerId());
  const columns = spreadsheetAccess.sheet.getRange(5, 2, 5, 100).getValues()[0];
  for (let i = 0; i < columns.length; i++) {
    if (columns[i].length == 0 || columns[i] == 'Results') {
      totalColumns = i;
      break;
    }
  }
  if (columns[totalColumns] != 'Results') {
    spreadsheetAccess.sheet.getRange(5, totalColumns + 2, 1, 1).
        setValue('Results');
  }
  // clear the results column
  spreadsheetAccess.sheet.getRange(6, totalColumns + 2, 1000, 1).clear();

  let row = spreadsheetAccess.nextRow();

  while (row != null) {
    let argument;
    let stopLimit;
    try {
      argument = parseArgument(row);
      stopLimit = parseStopLimit(row);
    } catch (ex) {
      logError(ex);
      row = spreadsheetAccess.nextRow();
      continue;
    }
    let selector = AdsApp.keywords();
    for (let i = 3; i < totalColumns; i++) {
      let header = columns[i];
      let value = row[i];
      if (!isNaN(parseFloat(value)) || value.length > 0) {
        if (header.indexOf("'") > 0) {
          value = value.replace(/\'/g, "\\'");
        } else if (header.indexOf('\"') > 0) {
          value = value.replace(/"/g, '\\\"');
        }
        const condition = header.replace('?', value);
        selector.withCondition(condition);
      }
    }
    selector.forDateRange(spreadsheetAccess.spreadsheet.
        getRangeByName('date_range').getValue());

    const keywords = selector.get();

    try {
      keywords.hasNext();
    } catch (ex) {
      logError(ex);
      row = spreadsheetAccess.nextRow();
      continue;
    }

    let fetched = 0;
    let changed = 0;

    for (const keyword of keywords) {
      let oldBid = keyword.bidding().getCpc();
      let action = row[0];
      let newBid;

      fetched++;
      if (action == 'Add') {
        newBid = addToBid(oldBid, argument, stopLimit);
      } else if (action == 'Multiply by') {
        newBid = multiplyBid(oldBid, argument, stopLimit);
      } else if (action == 'Set to First Page Cpc' ||
        action == 'Set to Top of Page Cpc') {
        let newBid = action == 'Set to First Page Cpc' ?
            keyword.getFirstPageCpc() : keyword.getTopOfPageCpc();
        let isPositive = newBid > oldBid;
        newBid = applyStopLimit(newBid, stopLimit, isPositive);
      }
      if (newBid < 0) {
        newBid = 0.01;
      }
      newBid = newBid.toFixed(2);
      if (newBid != oldBid) {
        changed++;
      }
      keyword.bidding().setCpc(parseFloat(newBid));
    }
    logResult('Fetched ' + fetched + '\nChanged ' + changed);

    row = spreadsheetAccess.nextRow();
  }

  spreadsheetAccess.spreadsheet.getRangeByName('last_execution')
      .setValue(new Date());
}

/**
 * Performs addition on oldbid and argument.
 *
 * @param {string} oldBid The old bid value.
 * @param {string} argument The arugument value in the spreadsheet.
 * @param {string} stopLimit The changes made to the bids.
 * @return {string} Applies stop limit to the bid.
 */
function addToBid(oldBid, argument, stopLimit) {
  return applyStopLimit(oldBid + argument, stopLimit, argument > 0);
}

/**
 * Performs multiplication on oldbid and argument.
 *
 * @param {string} oldBid The old bid value.
 * @param {string} argument The arugument value in the spreadsheet.
 * @param {string} stopLimit The changes made to the bids.
 * @return {string} Applies the stop limit to the bid.
 */
function multiplyBid(oldBid, argument, stopLimit) {
  return applyStopLimit(oldBid * argument, stopLimit, argument > 1);
}

/**
 * Applies stop limit to the bid depending on the conditions.
 *
 * @param {string} newBid The calculated bid value as per the action
 *   in the spreadsheet.
 * @param {string} stopLimit The changes made to the bids.
 * @param {boolean} isPositive checks for the value sign.
 * @return {string} assigns stop limit to the bids and returns.
 */
function applyStopLimit(newBid, stopLimit, isPositive) {
  if (stopLimit) {
    if (isPositive && newBid > stopLimit) {
      newBid = stopLimit;
    } else if (!isPositive && newBid < stopLimit) {
      newBid = stopLimit;
    }
  }
  return newBid;
}

/**
 * If the argument is not specified or bad arguments are passed, an error is
 * returned in the result field.
 *
 * @param {!Object} row The row in the spreadsheet.
 * @return {string} Returns error message in the result column.
 */
function parseArgument(row) {
  if (row[1].length == 0 && (row[0] == 'Add' || row[0] == 'Multiply by')) {
    throw ('\"Argument\" must be specified.');
  }
  let argument = parseFloat(row[1]);
  if (isNaN(argument)) {
    throw 'Bad Argument: must be a number.';
  }
  return argument;
}

/**
 * Parses stop limit from the spreadsheet.
 *
 * @param {!Object} row The row in the spreadsheet.
 * @return {string} Returns error message to the Result field in the row
 */
function parseStopLimit(row) {
  if (row[2].length == 0) {
    return null;
  }
  let limit = parseFloat(row[2]);
  if (isNaN(limit)) {
    throw 'Bad Argument: must be a number.';
  }
  return limit;
}

/**
 * Format the error messages in the spreadsheet
 *
 * @param {string} error The error messages.
 */
function logError(error) {
  spreadsheetAccess.sheet.getRange(spreadsheetAccess.currentRow(),
      totalColumns + 2, 1, 1)
  .setValue(error)
  .setFontColor('#c00')
  .setFontSize(8)
  .setFontWeight('bold');
}

/**
 * Formats the result messages in the spreadsheet
 *
 * @param {string} result The result values.
 */
function logResult(result) {
  spreadsheetAccess.sheet.getRange(spreadsheetAccess.currentRow(),
      totalColumns + 2, 1, 1)
  .setValue(result)
  .setFontColor('#444')
  .setFontSize(8)
  .setFontWeight('normal');
}

/**
 * Provides access to the spreadsheet using spreadsheetUrl, sheetName
 * and validate the spreadsheet.
 *
 * @param {string} spreadsheetUrl The spreadsheet url.
 * @param {string} sheetName The spreadsheet name.
 * @return {string} Returns spreadsheet along with rows.
 */
function SpreadsheetAccess(spreadsheetUrl, sheetName) {
  Logger.log('Using spreadsheet - %s.', spreadsheetUrl);
  this.spreadsheet = validateAndGetSpreadsheet(spreadsheetUrl);

  this.sheet = this.spreadsheet.getSheetByName(sheetName);
  this.cells = this.sheet.getRange(6, 2, this.sheet.getMaxRows(),
      this.sheet.getMaxColumns()).getValues();
  this.rowIndex = 0;

  this.nextRow = function() {
    for (; this.rowIndex < this.cells.length; this.rowIndex++) {
      if (this.cells[this.rowIndex][0]) {
        return this.cells[this.rowIndex++];
      }
    }
    return null;
  };
  this.currentRow = function() {
    return this.rowIndex + 5;
  };
}

/**
 * Validates the provided spreadsheet URL
 * to make sure that it's set up properly. Throws a descriptive error message
 * if validation fails.
 *
 * @param {string} spreadsheeturl The URL of the spreadsheet to open.
 * @return {Spreadsheet} The spreadsheet object itself, fetched from the URL.
 * @throws {Error} If the spreadsheet URL hasn't been set
 */
function validateAndGetSpreadsheet(spreadsheeturl) {
  if (spreadsheeturl == 'YOUR_SPREADSHEET_URL') {
    throw new Error('Please specify a valid Spreadsheet URL. You can find' +
        ' a link to a template in the associated guide for this script.');
  }
  return SpreadsheetApp.openByUrl(spreadsheeturl);
}