Bodaanpassingen voor apparaten

Voer bodaanpassingen voor apparaten automatisch door voor alle campagnes. Op basis van CPA wordt bepaald of biedingen verhoogd of verlaagd moeten worden.

Start nu!
Bodaanpassingen Get started!

Omdat de prestaties voor campagnes nogal kunnen verschillen per apparaat is het mogelijk om bodaanpassingen door te voeren voor desktop, mobiel en tablet. Deze gebruiken wij vaak wanneer er een significant verschil is in de CPA of ROAS tussen de verschillende apparaten. Maar in plaats van elke week alle campagnes nalopen, hebben we dit proces geautomatiseerd.

We hebben ook scripts voor bodaanpassingen op doelgroepen, locaties en voor elk uur van de dag. Start gelijk met het automatiseren van alle bodaanpassingen in jouw accounts!

Bodaanpassing op basis van CPA of ROAS

Filter apparaten per campagnes door een minimaal aantal conversies, conversiewaarde, kosten of klikken te specificeren in het script. Een bodaanpassing wordt alleen toegepast op een apparaat wanneer aan alle minimum waarden voldaan is. Je bepaalt zelf wat de maximale en minimale bodaanpassing is. Daarnaast kijkt het script alleen naar campagnes met een handmatige biedstrategie, omdat bodaanpassingen bij Smart Bidding al automatisch gebeuren.

Kies daarnaast om de bodaanpassing te berekenen op basis van CPA of ROAS. De CPA of ROAS per apparaat wordt vergeleken met de CPA of ROAS van de bijbehorende campagne.

Instellingen

In het script kan je verschillende instellingen aanpassen naar wens:

  • 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 conversies en kosten over deze periode.
  • MINIMUM_CONVERSIONS: Minimaal aantal conversies. Wanneer een apparaat minder conversies heeft, wordt er geen bodaanpassing gedaan.
  • MINIMUM_CONVERSIONVALUE: Minimale conversiewaarde. Wanneer een apparaat minder conversiewaarde heeft, wordt er geen bodaanpassing gedaan.
  • MINIMUM_COST: Minimale kosten voor een apparaat. Wanneer een apparaat minder uitgaven heeft, wordt er geen bodaanpassing gedaan.
  • MINIMUM_CLICKS: Minimaal aantal klikken. Wanneer een apparaat 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%.

Frequentie: Om voldoende data te kunnen verzamelen, raden we aan om dit script dagelijks of wekelijks te draaien.

The script
// Copyright 2021. Increase BV. All Rights Reserved.
//
// Created By: Tibbe van Asten
// for Increase B.V.
// 
// Created: 01-03-2018
// Last update: 23-03-2021
//
// ABOUT THE SCRIPT
// With this script we adjust the biddings for devices for
// all active campaigns in the account. Change will be based
// on the device statistics vs campaign statistics.
//
////////////////////////////////////////////////////////////////////

var config = {

  LOG : true,
  DATE_RANGE : "LAST_30_DAYS",

  // Optional: Use minimum conversions and/or cost to select targetlocations
  // Leave empty to skip
  MINIMUM_CONVERSIONS : "",
  MINIMUM_CONVERSIONVALUE : "",
  MINIMUM_COST : "10",
  MINIMUM_CLICKS : "",
  
  // Set bidadjustments based on CPA or ROAS
  KPI : "ROAS",
  
  // Optional: Use a campaignlabel to make a selection.
  // Leave empty to skip
  CAMPAIGN_LABEL : "ROAS",
  
  // 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 device data
  //var report = selectDevices();
  
  var report = AdsApp.report(
    "SELECT CampaignName, CampaignId, Device, CampaignDesktopBidModifier, CampaignMobileBidModifier, CampaignTabletBidModifier, Labels, Conversions, ConversionValue, Cost, Clicks " +
    "FROM CAMPAIGN_PERFORMANCE_REPORT " +
    "WHERE BiddingStrategyType IN ['MANUAL_CPC','MANUAL_CPV','MANUAL_CPM'] " +
    "AND CampaignStatus = ENABLED " +
    "DURING " + config.DATE_RANGE
  );

  var rows = report.rows();
  while(rows.hasNext()){
    var row = rows.next();

    // Check campaign label
    if(config.CAMPAIGN_LABEL != "" && row["Labels"].indexOf(config.CAMPAIGN_LABEL) < 0) continue;
    
    // 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;
    }
      
    var campaignKpi = parseFloat(getCampaignKpi(row["CampaignId"])).toFixed(2);
    
    // Select old bidModifier
    if(row["Device"] == "DESKTOP"){ var oldBidModifier = row["CampaignDesktopBidModifier"] };
    if(row["Device"] == "MOBILE"){ var oldBidModifier = row["CampaignMobileBidModifier"] };
    if(row["Device"] == "TABLET"){ var oldBidModifier = row["CampaignTabletBidModifier"] };
    
    if(oldBidModifier){
      var oldBid = parseFloat(1 + (oldBidModifier.split("%")[0] / 100)).toFixed(2);
    } else {
      var oldBid = 1;
    }
    
    // Calculate KPI's and new bid
    if(config.KPI == "CPA"){ var deviceKpi = parseFloat(row["Cost"] / row["Conversions"]).toFixed(2); var newBid = parseFloat(campaignKpi / deviceKpi).toFixed(2); }
    if(config.KPI == "ROAS"){ var deviceKpi = parseFloat(row["ConversionValue"] / row["Cost"]).toFixed(2); var newBid = parseFloat(deviceKpi / campaignKpi).toFixed(2); }
    
    // If there is a CPA or ROAS, we will set a bidadjustment
    if(isNaN(newBid) === false && isFinite(deviceKpi) === 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["Device"] + "," + row["CampaignId"]] == null) {
          map[row["Device"] + "," + row["CampaignId"]] = [];
        }
        map[row["Device"] + "," + row["CampaignId"]].push([row["Device"], row["CampaignName"], newBid]);
      
          if(config.LOG === true){
            Logger.log(row["Device"] + " in " + row["CampaignName"]);
            Logger.log("Current bidadjustment: " + oldBidModifier + " = " + oldBid);
            Logger.log("Device " + config.KPI + ": " + deviceKpi + ", Campaign " + config.KPI + ": " + campaignKpi + ", New bid: " + newBid);
            Logger.log("------------");
          }   
      }

    } // Check bid
    
  } // rowIterator
  
  if(config.LOG === true){
    Logger.log(" ");
    Logger.log("...Loop through devices");
    Logger.log(" ");
  }
  
  for (var key in map) {
    
    var campaignIterator = AdsApp
      .campaigns()
      .withIds([key.split(",")[1]])
      .get();
    
    while(campaignIterator.hasNext()){
      var campaign = campaignIterator.next();
      
      Logger.log(campaign.getName());
      Logger.log(key.split(",")[0]);
      
      var deviceSelector = campaign.targeting().platforms();
      
      if(key.split(",")[0] == "Computers"){ var deviceIterator = deviceSelector.desktop().get(); }
      if(key.split(",")[0] == "Mobile devices with full browsers"){ var deviceIterator = deviceSelector.mobile().get(); }
      if(key.split(",")[0] == "Tablets with full browsers"){ var deviceIterator = deviceSelector.tablet().get(); }
      if(key.split(",")[0] != "Computers" && key.split(",")[0] != "Mobile devices with full browsers" && key.split(",")[0] != "Tablets with full browsers") continue;
      
      while(deviceIterator.hasNext()){
        var device = deviceIterator.next();
        
        device.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]);
        
      } // deviceIterator
      
    } // campaignIterator
    
  } // keyIterator
  
} // function main()

////////////////////////////////////////////////////////////////////

function selectDevices(){

  if(config.CAMPAIGN_LABEL == ""){
    var report = AdsApp.report(
      "SELECT CampaignName, CampaignId, Device, CampaignDesktopBidModifier, CampaignMobileBidModifier, CampaignTabletBidModifier, Conversions, ConversionValue, Cost, Clicks " +
      "FROM CAMPAIGN_PERFORMANCE_REPORT " +
      "WHERE BiddingStrategyType IN ['MANUAL_CPC','MANUAL_CPV','MANUAL_CPM'] " +
      "AND CampaignStatus = ENABLED " +
      "DURING " + config.DATE_RANGE
    );
  } else {
    var report = AdsApp.report(
      "SELECT CampaignName, CampaignId, Device, CampaignDesktopBidModifier, CampaignMobileBidModifier, CampaignTabletBidModifier, Conversions, ConversionValue, Cost, Clicks " +
      "FROM CAMPAIGN_PERFORMANCE_REPORT " +
      "WHERE BiddingStrategyType IN ['MANUAL_CPC','MANUAL_CPV','MANUAL_CPM'] " +
      "AND CampaignStatus = ENABLED " +
      "AND Labels CONTAINS_ANY ['" + config.CAMPAIGN_LABEL + "'] " +
      "DURING " + config.DATE_RANGE
    );
  }
  
  return report;    
  
} // function deviceSelector

////////////////////////////////////////////////////////////////////

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 Asten Team Lead Performance Marketing
Nils Rooijmans Water Cooler Topics
Martijn Kraan Freelance PPC Specialist
Bas Baudoin Teamlead SEA @ Happy Leads
Jermaya Leijen Digital Marketing Strategist
Krzysztof Bycina PPC Specialist from Poland
How about you? JOIN US!
Sharing Knowledge
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.

Neem contact op

Training &
Workshop
Neem contact op!
Adsscripts Training & Workshop