Wanneer je bodaanpassingen op elk uur van de dag teveel van het goede vindt, biedt dit script de oplossing. Net zoals de scripts voor bodaanpassingen op apparaten, locaties en doelgroepen, worden hiermee automatisch bodaanpassingen ingesteld voor alle blokken in de advertentieplanning van een campagne. Deze advertentieplanning kan je naar wens indelen per campagne.
Met de laatste aanpassingen in dit script is het mogelijk om de bodaanpassingen te berekenen op basis van CPA of ROAS. De CPA of ROAS van een locatie wordt vergeleken met de CPA of ROAS van de bijbehorende campagne.
Instellingen
LOG: Geef aan of het script de tussenstappen moet rapporteren, door de waarde aan te passen naar 'true'.
DATE_RANGE: Het script kijkt naar de statistieken over deze periode.
MINIMUM_CONVERSIONS: Minimaal aantal conversies. Wanneer een blok minder conversies heeft, wordt er geen bodaanpassing gedaan.
MINIMUM_CONVERSIONVALUE: Minimale conversiewaarde. Wanneer een blok minder conversiewaarde heeft, wordt er geen bodaanpassing gedaan.
MINIMUM_COST: Minimale kosten voor een blok. Wanneer een blok minder uitgaven heeft, wordt er geen bodaanpassing gedaan.
MINIMUM_CLICKS: Minimaal aantal klikken. Wanneer een blok minder klikken heeft, wordt er geen bodaanpassing gedaan.
KPI: Kies om de bodaanpassing te berekenen op basis van 'CPA' of 'ROAS'
CAMPAIGN_LABEL: Gebruik een label om campagnes te selecteren
MAX_BID: De bodaanpassing zal niet hoger zijn dan dit ingestelde bod. 1.3 = +30% of 2 = +100%
MIN_BID: De bodaanpassing zal niet lager zijn dan dit ingestelde bod. 0.75 = -25% of 0.5 = -50%
The script
// Copyright 2021. Increase BV. All Rights Reserved.
//
// Created By: Tibbe van Asten
// for Increase B.V.
//
// Created: 19-03-2020
// Last update: 09-11-2021
//
// ABOUT THE SCRIPT
// With this script we adjust the biddings for target adschedule in
// active campaigns.
//
////////////////////////////////////////////////////////////////////
var config = {
LOG : true,
DATE_RANGE : "LAST_30_DAYS",
// Optional: Use minimum conversions and/or cost to select target adschedules
// Leave empty to skip
MINIMUM_CONVERSIONS : "",
MINIMUM_CONVERSIONVALUE : "",
MINIMUM_COST : "",
MINIMUM_CLICKS : "",
// Set bidadjustments based on CPA or ROAS
KPI : "CPA",
// Optional: Use a campaignlabel to make a selection.
// Leave empty to skip
CAMPAIGN_LABEL : "",
// Bidadjustments won't be higher then MAX_BID and not lower then MIN_BID
// Examples: 1.2 = +20%, 0.8 = -20%, 2 = +100%
MAX_BID : 1.2,
MIN_BID : 0.8
}
////////////////////////////////////////////////////////////////////
function main() {
if(config.KPI != "CPA" && config.KPI != "ROAS"){
throw Error("Set KPI to 'CPA' or 'ROAS' only!");
}
var map = {};
// Collecting all target ad schedule data
var report = AdsApp.report(
"SELECT Id, CampaignName, CampaignId, BidModifier, Conversions, ConversionValue, Cost, Clicks " +
"FROM CAMPAIGN_AD_SCHEDULE_TARGET_REPORT " +
"WHERE CampaignStatus = ENABLED " +
" DURING " + config.DATE_RANGE
);
var rows = report.rows();
while(rows.hasNext()){
var row = rows.next();
// Check thresholds
if(config.MINIMUM_CONVERSIONS != "" || config.MINIMUM_CONVERSIONVALUE != "" ||config.MINIMUM_COST != "" || config.MINIMUM_CLICKS != ""){
if(row["Conversions"] < config.MINIMUM_CONVERSIONS || row["ConversionValue"] < config.MINIMUM_CONVERSIONVALUE || row["Cost"] < config.MINIMUM_COST || row["Clicks"] < config.MINIMUM_CLICKS) continue;
}
// Skip campaign when not set to manual bidding. And check label.
var campaignIterator = AdsApp.campaigns().withIds([row["CampaignId"]]).get();
while(campaignIterator.hasNext()){
var campaign = campaignIterator.next();
if(campaign.bidding().getStrategyType() != "MANUAL_CPC" && campaign.bidding().getStrategyType() != "MANUAL_CPM" && campaign.bidding().getStrategyType() != "MANUAL_CPV") continue;
if(config.CAMPAIGN_LABEL != ""){ while(!campaign.labels().withCondition("Name = '" + config.CAMPAIGN_LABEL + "'").get().hasNext()) continue; }
var campaignKpi = parseFloat(getCampaignKpi(campaign.getId())).toFixed(2);
var oldBid = parseFloat(1 + (row["BidModifier"].split("%")[0] / 100)).toFixed(2);
// Calculate KPI's and new bid
if(config.KPI == "CPA"){ var scheduleKpi = parseFloat(row["Cost"] / row["Conversions"]).toFixed(2); var newBid = parseFloat(campaignKpi / scheduleKpi).toFixed(2); }
if(config.KPI == "ROAS"){ var scheduleKpi = parseFloat(row["ConversionValue"] / row["Cost"]).toFixed(2); var newBid = parseFloat(scheduleKpi / campaignKpi).toFixed(2); }
// If there is a CPA or ROAS, we will set a bidadjustment
if(isNaN(newBid) === false && isFinite(scheduleKpi) === true) {
if(newBid < config.MIN_BID) { newBid = parseFloat(config.MIN_BID).toFixed(2); }
if(newBid > config.MAX_BID) { newBid = parseFloat(config.MAX_BID).toFixed(2); }
// Add to map
if(newBid != oldBid) {
if(map[row["Id"] + "," + row["CampaignId"]] == null) {
map[row["Id"] + "," + row["CampaignId"]] = [];
}
map[row["Id"] + "," + row["CampaignId"]].push([row["Id"], row["CampaignName"], newBid]);
if(config.LOG === true){
Logger.log("Ad Schedule " + row["Id"] + " in " + row["CampaignName"]);
Logger.log("Current bidadjustment: " + row["BidModifier"] + " = " + oldBid);
Logger.log("Ad Schedule " + config.KPI + ": " + scheduleKpi + ", Campaign " + config.KPI + ": " + campaignKpi + ", New bid: " + newBid);
Logger.log("------------");
}
} // Add to map
} // Check bidadjustment
} // campaignIterator
} // rowIterator
if(config.LOG === true){
Logger.log(" ");
Logger.log("...Loop through ad schedules");
Logger.log(" ");
}
for (var key in map) {
var campaignIterator = AdsApp
.campaigns()
.withIds([key.split(",")[1]])
.get();
while(campaignIterator.hasNext()){
var campaign = campaignIterator.next();
var ids = [];
ids.push(key.split(",")[1]);
ids.push(key.split(",")[0]);
var schedulesIterator = campaign
.targeting()
.adSchedules()
.withIds([ids])
.get();
while(schedulesIterator.hasNext()){
var schedule = schedulesIterator.next();
schedule.setBidModifier(parseFloat(map[key][0][2]));
Logger.log("Bidadjustment of " + map[key][0][2] + " set for " + map[key][0][0] + " in " + map[key][0][1]);
} // locationIterator
} // campaignIterator
} // keyIterator
} // function main()
////////////////////////////////////////////////////////////////////
function getCampaignKpi(campaignId){
var report = AdsApp.report(
"SELECT Cost, Conversions, ConversionValue " +
"FROM CAMPAIGN_PERFORMANCE_REPORT " +
"WHERE CampaignId = '" + campaignId + "' " +
"DURING " + config.DATE_RANGE
);
var rows = report.rows();
while(rows.hasNext()){
var campaign = rows.next();
if(config.KPI == "CPA"){ var campaignKpi = campaign["Cost"] / campaign["Conversions"]; }
if(config.KPI == "ROAS"){ var campaignKpi = campaign["ConversionValue"] / campaign["Cost"]; }
} // campaignIterator
return campaignKpi;
} // function getCampaignKpi()
Show whole script!
Loading Comments
The Experts
Tibbe van AstenTeam Lead Performance Marketing
Nils RooijmansWater Cooler Topics
Martijn KraanFreelance PPC Specialist
Bas BaudoinTeamlead SEA @ Happy Leads
Jermaya LeijenDigital Marketing Strategist
Krzysztof BycinaPPC Specialist from Poland
How about you?JOIN US!
Caring
Adsscripts.com staat voor het delen van kennis. In de huidige markt houden SEA-specialisten de kennis en ervaring graag voor zich. Wij zijn er van overtuigd dat het delen van kennis ervoor kan zorgen dat iedereen beter wordt in haar of zijn werk. Daarom lopen wij hier graag in voorop, door onze kennis over scripts te delen met iedereen.
Wil jij ook graag een bijdrage leveren? Wij staan open voor nieuwe ideeën en feedback op alles wat je op Adsscripts.com vindt.