Automação Google Ads: Como programar contagem regressiva.

Nessa série especial, reunimos alguns scripts úteis para você automatizar e aumentar o retorno de suas campanhas no Google Ads. Confira abaixo como automatizar o Google Ads para “contagem regressiva”.

 

1. Personalizador de Anúncios – Por Google Ads. Seus anúncios terão dados atualizados em tempo real com o script do Google. O script busca seus dados em uma planilha do Google, como preço, estoque e outras variáveis e os disponibilizam em seus anúncios. Você pode criar um anúncio com preço atualizado em tempo real, por exemplo.

 

// 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 Ad Customizer
*
* @overview The Ad Customizer script shows how to setup parameterized ads in
* your account. This script generates ads for a hypothetical flower shop
* whose inventory data is stored in Google Spreadsheets. In real
* advertising campaigns, you'll need to substitute it with your own
* datasources. See
* https://developers.google.com/adwords/scripts/docs/solutions/customizer
* for more details.
*
* @author AdWords Scripts Team [[email protected]]
*
* @version 1.0.2
*
* @changelog
* - version 1.0.2
* - Added validation for external spreadsheet setup.
* - version 1.0.1
* - Updated to use expanded text ads.
* - version 1.0
* - Released initial version.
*/

// Replace this with the URL of the spreadsheet containing your data.
var SPREADSHEET_URL = 'YOUR_SPREADSHEET_URL';
// Replace this with the name of the campaign you want to use.
var CAMPAIGN = 'Campaign name';
// Replace this with the name of the ad group you want to use.
var ADGROUP = 'Flowers';

/**
* Retrieves an ad group given a campaign and ad group name.
*
* @param {string} campaignName The campaign name.
* @param {string} adGroupName The ad group name.
* @return {!AdGroup} The ad group.
* @throws if the specified campaign and ad group does not exist.
*/
function getAdGroup(campaignName, adGroupName) {
return AdWordsApp.adGroups()
.withCondition('Name = "' + adGroupName + '"')
.withCondition('CampaignName = "' + campaignName + '"')
.get()
.next();
}

/**
* Creates customized ads if they haven't already been created.
*
* @param {!AdGroup} adGroup The ad group within which to create the ads.
*/
function maybeCreateAds(adGroup) {
var ads = adGroup.ads().get();
while (ads.hasNext()) {
var ad = ads.next();
if (ad.isType().expandedTextAd()) {
var expandedTextAd = ad.asType().expandedTextAd();
if (expandedTextAd.getHeadlinePart1() == 'Flowers For Sale') {
// The ads have already been created; no need to do more
return;
}
}
}

// Reference the 'Flowers' data source here; text will be inserted when the
// ad is served.
adGroup.newAd().expandedTextAdBuilder()
.withHeadlinePart1('{=Flowers.name} For Sale')
.withHeadlinePart2('Fresh cut {=Flowers.name_lowercase}')
.withDescription('starting at {=Flowers.price}')
.withFinalUrl('http://example.com')
.build();
// All ad groups also need to have an ad without ad customizers to fall back
// on, in case no ad customizers are able to serve.
adGroup.newAd().expandedTextAdBuilder()
.withHeadlinePart1('Flowers For Sale')
.withHeadlinePart2('Fresh cut flowers')
.withDescription('delivered for cheap')
.withFinalUrl('http://www.example.com')
.build();
}

/**
* Retrieves or creates the Flowers data source.
*
* @return {!AdCustomizerSource}
*/
function getOrCreateDataSource() {
var sources = AdWordsApp.adCustomizerSources().get();
while (sources.hasNext()) {
var source = sources.next();
if (source.getName() == 'Flowers') {
return source;
}
}
return AdWordsApp.newAdCustomizerSourceBuilder()
.withName('Flowers')
.addAttribute('name', 'text')
.addAttribute('name_lowercase', 'text')
.addAttribute('price', 'price')
// Attributes named 'Custom ID' are special: the system will make sure
// that all values in the data source have unique custom IDs.
.addAttribute('Custom ID', 'text')
.build()
.getResult();
}

/**
* Reads flower / quantity / price data from a spreadsheet. In a real
* advertising campaign, you would have your own data source to read from.
*
* @param {string} url The URL of the spreadsheet to read from.
* @return {!Object} A lookup of flower details, by name.
*/
function readFlowers(url) {
var flowersByName = {};

Logger.log('Using spreadsheet - %s.', url);
var spreadsheet = validateAndGetSpreadsheet(url);

var sheet = spreadsheet.getSheets()[0];
var data = sheet.getRange(2, 1, sheet.getMaxRows() - 1, 3).getValues();
for (var i = 0; i < data.length; i++) {
if (data[i][0]) {
var flower = {
name: data[i][0],
quantity: parseFloat(data[i][1]),
price: data[i][2]
};
if (typeof flower.price != 'string') {
// Spreadsheets will sometimes coerce "$4.99" into just the number 4.99.
// In that case, add the dollar sign back.
flower.price = '$' + flower.price.toFixed(2);
}
flowersByName[flower.name] = flower;
}
}
return flowersByName;
}

/**
* Obtains a mapping from Customizer ID to Customizer object.
*
* @param {!AdCustomizerSource} source
* @return {!Object.<!AdCustomizerItem>} A mapping from custom ID to item.
*/
function getCustomizersById(source) {
var customizers = source.items().get();
var customizersById = {};
while (customizers.hasNext()) {
var customizer = customizers.next();
customizersById[customizer.getAttributeValue('Custom ID')] = customizer;
}
return customizersById;
}

/**
* Sets the price value of existing customizers, or creates new customizers
* where none exists.
*
* @param {!AdCustomizerSource} source
* @param {!Object} flowersInStock A mapping of flower names to objects
* objects containing flower details, including price.
* @param {!Object.<!AdCustomizerItem>} customizersById A mapping from custom ID
* to ad customizer.
* @return {!Object} The object representing the flower with most in stock.
*/
function setCustomizerValues(source, flowersInStock, customizersById) {
var mostInStock;
var highestStock = 0;
for (var flowerName in flowersInStock) {
var flower = flowersInStock[flowerName];
if (flower.quantity > highestStock) {
highestStock = flower.quantity;
mostInStock = flower;
}
var customizer = customizersById[flower.name.toLowerCase()];
if (customizer) {
customizer.setAttributeValue('price', flower.price);
} else {
source.adCustomizerItemBuilder()
.withAttributeValue('Custom ID', flower.name.toLowerCase())
.withAttributeValue('name', flower.name)
.withAttributeValue('name_lowercase', flower.name.toLowerCase())
.withAttributeValue('price', flower.price)
.withTargetKeyword(flower.name)
.build();
}
}
return mostInStock;
}

/**
* Main entry point.
*/
function main() {
var adGroup = getAdGroup(CAMPAIGN, ADGROUP);
var source = getOrCreateDataSource();
maybeCreateAds(adGroup);

// Get all customizer items in the 'Flowers' data source, and create a map
// from item ID to item.
var customizersById = getCustomizersById(source);

// For each flower in inventory, update the matching ad customizer item's
// 'price' attribute. Also find the flower with the highest quantity in stock.
var flowersInStock = readFlowers(SPREADSHEET_URL);
var mostInStock =
setCustomizerValues(source, flowersInStock, customizersById);

// Point the 'default' customizer item to the flower that has the largest
// quantity in stock. The default customizer item has no target keyword, so
// it'll be triggered for generic search terms like 'flowers'.
var defaultCustomizer = customizersById['Default'];
if (defaultCustomizer) {
defaultCustomizer.setAttributeValues({
name: mostInStock.name,
name_lowercase: mostInStock.name.toLowerCase(),
price: mostInStock.price
});
} else {
source.adCustomizerItemBuilder()
.withAttributeValue('Custom ID', 'Default')
.withAttributeValue('name', mostInStock.name)
.withAttributeValue('name_lowercase', mostInStock.name.toLowerCase())
.withAttributeValue('price', mostInStock.price)
.build();
}
}

/**
* DO NOT EDIT ANYTHING BELOW THIS LINE.
* Please modify your spreadsheet URL and email addresses at the top of the file
* only.
*/

/**
* Validates the provided spreadsheet URL and email address
* to make sure that they're 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 or email 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.');
}
var spreadsheet = SpreadsheetApp.openByUrl(spreadsheeturl);
return spreadsheet;
}

 

Como configurar 

 

2. Parametrizador de Anúncios – Por Google Ads. Assim como o script Personalizador de Anúncios, você pode fazê-lo com parâmetros de anúncios. esses scripts podem ser executados a nível da MCC controlando todas as contas que estão a baixo na estrutura.

Confira a documentação completa clicando aqui

3. Contagem Regressiva da Venda – Por Google Ads. Este é um ótimo script para dar um apelo de “urgência” ao seus anúncios, pois, ele permite que seja feita uma contagem regressiva sem a necessidade de criar novos anúncios toda vez que a contagem for reduzida.

// 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 Sales Countdown
*
* @overview The sales countdown script demonstrates how to update your ad
* parameters hourly to count down until a sale you'd like to promote. See
* https://developers.google.com/adwords/scripts/docs/solutions/sale-countdown
* for more details.
*
* @author AdWords Scripts Team [[email protected]]
*
* @version 1.0
*
* @changelog
* - version 1.0
* - Released initial version.
*/

// Date and time for the end of the sale. Be sure to include a time and timezone
// so that the sale ends exactly when you intend.
var END_DATE = new Date('February 17, 2016 13:00:00 -0500');
// Change this to the Ad Group you set up with text ads with AdParams.
var AD_GROUP_NAME = 'Widget Sale';

var DAY_IN_MILLISECONDS = 24 * 60 * 60 * 1000;

function main() {
var timeLeft = calculateTimeLeftUntil(END_DATE);
var adGroup = getAdGroup(AD_GROUP_NAME);
var keywords = adGroup.keywords().get();
while (keywords.hasNext()) {
var keyword = keywords.next();
// We want to update {param1} to use our calculated days and {param2}
// for hours.
keyword.setAdParam(1, timeLeft['days']);
keyword.setAdParam(2, timeLeft['hours']);
}
}

function calculateTimeLeftUntil(end) {
var current = new Date();
var timeLeft = {};
var daysFloat = (end - current) / (DAY_IN_MILLISECONDS);
timeLeft['days'] = Math.floor(daysFloat);
timeLeft['hours'] = Math.floor(24 * (daysFloat - timeLeft['days']));

// Prevent countdown to negative time period.
if (timeLeft['days'] < 0) {
timeLeft['days'] = 0;
}
if (timeLeft['hours'] < 0) {
timeLeft['hours'] = 0;
}

return timeLeft;
}

function getAdGroup(name) {
var adGroupIterator = AdWordsApp.adGroups()
.withCondition('Name = "' + name + '"')
.withLimit(1)
.get();
if (adGroupIterator.hasNext()) {
return adGroupIterator.next();
}
}

Como configurar

 

4. Agenda de Contagem Regressiva da Venda – Por Google Ads. Da mesma forma que o script da Contagem Regressiva da Venda, esse script permite essa contagem mas estendendo para vários eventos usando o Personalizador de Anúncios.

 

// 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 Sales Countdown Calendar
*
* @overview The Sales Countdown Calendar script allows you to update ads in
* your account to count down to multiple sales events by integrating a
* calendar to the script, and using ad customizers. See
* https://developers.google.com/adwords/scripts/docs/solutions/sales-countdown-calendar
* for more details.
*
* @author AdWords Scripts Team [[email protected]]
*
* @version 1.0
*
* @changelog
* - version 1.0
* - Released initial version.
*/

// Set this value to your calendar's ID.
var CALENDAR_ID = 'INSERT_CALENDAR_ID';

// Name of the ad customizer source created by this script. If you change this
// value, you should also change your ads to refer to the new customizer name.
var CUSTOMIZER_NAME = 'SalesCountdown';

// Set this value to receive an email whenever the script updates ad
// customizers in your account.
var EMAIL = '[email protected]';

// Set this value to the list of users who should get updates whenever the
// script updates ad customizers in your account.
var EMAIL_CC = ['[email protected]', '[email protected]'];

function main() {
var events = listAllEvents();
var now = new Date();
var customizer = getAdCustomizerSource();
if (!customizer) {
customizer = createAdCustomizerSource();
}

var logs = [];

for (var i = 0; i < events.length; i++) {
var event = events[i];
if (event.Campaign) {
var customizerItem = getCustomizerItem(customizer, event.Name);

if (customizerItem) {
if (event.EndDate > now) {
if (customizerItem.getAttributeValue('EndDate') !=
formatDate(event.EndDate) ||
customizerItem.getTargetCampaignName() != event.Campaign) {
setCustomizerItem(customizerItem, event.StartDate,
event.EndDate, event.Campaign);

logs.push(Utilities.formatString('Updated countdown entry ' +
'for %s. New end date: %s, associated campaign: %s.',
event.Name, event.EndDate, event.Campaign));
}
} else {
removeCustomizerItem(customizerItem);
logs.push(Utilities.formatString('Removed countdown entry for ' +
'expired event: %s.', event.Name));
}
} else {
if (event.EndDate > now) {
addCustomizerItem(customizer, event.Name, event.StartDate,
event.EndDate, event.Campaign);
logs.push(Utilities.formatString('Added countdown entry for %s. ' +
'End date: %s, associated campaign: %s.', event.Name,
event.EndDate, event.Campaign));
}
}
}
}

if (logs.length > 0) {
var htmlBody = [];
htmlBody.push('The Sales countdown calendar script made the following ' +
'changes to Customer ID: ' +
AdWordsApp.currentAccount().getCustomerId() +
'<br>');
htmlBody.push('<ul>');
for (var i = 0; i < logs.length; i++) {
htmlBody.push('<li>' + logs[i] + '</li>' + '<br>');
}
htmlBody.push('</ul>');
MailApp.sendEmail({
to: EMAIL,
cc: EMAIL_CC.join(','),
subject: 'Sales countdown calendar',
htmlBody: htmlBody.join('\n')
});
}
}

/**
* Gets the campaign name associated with an event.
*
* @param {string} eventDescription The event description.
*
* @return {?(string )} Name of the campaign associated with this event,
* or null if the event description doesn't specify one.
*/
function getCampaignName(eventDescription) {
var parts = eventDescription.split('\n');
for (var i = 0; i < parts.length; i++) {
var subparts = parts[i].split('=');
if (subparts.length == 2 && subparts[0].toLowerCase() == 'campaign') {
return subparts[1];
}
}
return null;
}

/**
* Removes an ad customizer item.
*
* @param {AdCustomizerItem} customizerItem The item to be removed.
*/
function removeCustomizerItem(customizerItem) {
customizerItem.remove();
}

/**
* Retrieves an ad customizer item by its event name.
*
* @param {AdCustomizerSource} adCustomizerSource The ad customizer source to
* search for the item.
* @param {string} eventName The event name.
*
* @return {?(AdCustomizerItem )} The ad customizer item if it exists,
* null otherwise.
*/
function getCustomizerItem(adCustomizerSource, eventName) {
var items = adCustomizerSource.items().get();

while (items.hasNext()) {
var item = items.next();
if (item.getAttributeValue('EventName') == eventName) {
return item;
}
}
return null;
}

/**
* Updates an ad customizer item with new event details.
*
* @param {AdCustomizerItem} customizerItem The ad customizer item to
* be updated.
* @param {Date} startDate The event start date.
* @param {Date} endDate The event end date.
* @param {string} campaignName The name of the campaign this event is
* associated with.
*/
function setCustomizerItem(customizerItem, startDate, endDate, campaignName) {
customizerItem.setAttributeValue('StartDate', formatDate(startDate));
customizerItem.setAttributeValue('EndDate', formatDate(endDate));
customizerItem.setTargetCampaign(campaignName);
}

/**
* Adds an ad customizer item.
*
* @param {AdCustomizerSource} adCustomizerSource The ad customizer source to
* which the new item is added.
* @param {string} eventName The event name.
* @param {Date} startDate The event start date.
* @param {Date} endDate The event end date.
* @param {string} campaignName The name of the campaign this event is
* associated with.
*
* @return {AdCustomizerItem} The ad customizer item.
*/
function addCustomizerItem(adCustomizerSource, eventName, startDate, endDate,
campaignName) {
var operation = adCustomizerSource.adCustomizerItemBuilder()
.withAttributeValue('StartDate', formatDate(startDate))
.withAttributeValue('EndDate', formatDate(endDate))
.withAttributeValue('EventName', eventName)
.withTargetCampaign(campaignName)
.build();
return operation.getResult();
}

/**
* Formats a date for creating a ad customizer item.
*
* @param {string} date The date to be formatted.
*
* @return {string} The formatted date.
*/
function formatDate(date) {
return Utilities.formatDate(date, AdWordsApp.currentAccount().getTimeZone(),
'yyyyMMdd HHmmss');
}

/**
* Create a new ad customizer source for this script.
*
* @return {(AdCustomizerSource)} The new ad customizer source.
*/
function createAdCustomizerSource() {
var operation = AdWordsApp.newAdCustomizerSourceBuilder()
.addAttribute('StartDate', 'date')
.addAttribute('EndDate', 'date')
.addAttribute('EventName', 'text')
.withName(CUSTOMIZER_NAME)
.build();
return operation.getResult();
}

/**
* Gets the ad customizer source for this script.
*
* @return {?(AdCustomizerSource )} An ad customizer source,
* if it exists, null otherwise.
*/
function getAdCustomizerSource() {
var customizers = AdWordsApp.adCustomizerSources().get();
while (customizers.hasNext()) {
var customizer = customizers.next();
if (customizer.getName() == CUSTOMIZER_NAME) {
return customizer;
}
}
return null;
}

/**
* Gets the calendar's timezone.
*
* @return {string} The calendar's timezone.
*/
function getCalendarTimezone() {
var calendar = Calendar.Calendars.get(CALENDAR_ID);
return calendar.timeZone;
}

/**
* Lists all events on the calendar.
*
* @return {Array.<Object>} An array of event details.
*/
function listAllEvents() {
var calendarEvents = Calendar.Events.list(CALENDAR_ID, {
singleEvents: true,
orderBy: 'startTime'
});

var retval = [];

for (var i = 0; i < calendarEvents.items.length; i++) {
var event = calendarEvents.items[i];
if (event.start.date || event.end.date) {
throw ('All day events are not supported. Set a start and end time.');
}
var startDate = parseDate(event.start.dateTime);
var endDate = parseDate(event.end.dateTime);

retval.push({
'Name': event.summary,
'Campaign': getCampaignName(event.description),
'StartDate': startDate,
'EndDate': endDate
});
}
return retval;
}

/**
* Parses a date from Google Calendar.
*
* @param {string} dateText The date as a string.
*
* @return {Date} the parsed date.
*/
function parseDate(dateText) {
// Date format is yyyy-mm-ddTHH:mm:ss±HH:mm.
var dateFormat = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})([+-]\d{2}):(\d{2})$/;
var parts = dateText.match(dateFormat);

var year = parts[1];
var month = parts[2];
var day = parts[3];
var hour = parts[4];
var minute = parts[5];
var second = parts[6];
var tzHour = parseInt(parts[7]);
var tzMin = parseInt(parts[8]);

// Change the sign of tzMin if tzHour is negative. This way, -05:30
// is interpreted as -05:00 -00:30 instead of -05:00 -00:30
if (tzHour < 0) {
tzMin = -tzMin;
}
var tzOffset = new Date().getTimezoneOffset() + tzHour * 60 + tzMin;

return new Date(year, month - 1, day, hour, minute - tzOffset, second, 0);
}

Como configurar