Bidadjustments for devices

Automatically review device bid adjustments for all campaigns. Based on CPA, it is determined whether bids should be increased or decreased.

Start Now!
Bidadjustments Get started!

Because the performance for campaigns can vary considerably per device, it is possible to make bid adjustments for desktop, mobile and tablet. We often use these when there is a significant difference in the CPA or ROAS between the different devices. But instead of checking all campaigns every week, we have automated this process.

We also have scripts for bidadjustments for audiences, locations or adschedule. Start automating all of your bidadjustments today.

Bid adjustment based on CPA or ROAS

Filter devices by campaigns by specifying a minimum number of conversions, conversion value, costs or clicks in the script. A bid adjustment is only applied to a device when all minimum values ​​are met. You decide what the maximum and minimum bid adjustment is. In addition, the script only looks at campaigns with a manual bidding strategy, because bid adjustments with Smart Bidding are already done automatically.

In addition, choose to calculate the bid adjustment based on CPA or ROAS. The CPA or ROAS per device is compared to the CPA or ROAS of the corresponding campaign.

Settings

In the script you can adjust various settings as desired:

  • LOG: Specify whether the script should report the intermediate steps by adjusting the value to 'true'.
  • DATE_RANGE: The script looks at the statistics over this period.
  • MINIMUM_CONVERSIONS: Set a minimum number of conversions for a device. If a device has less conversions, we won't set a bidadjustment.
  • MINIMUM_CONVERSIONVALUE: Set a minimum conversionvalue for a device. If a device has less revenue, we won't set a bidadjustment.
  • MINIMUM_COST: Set a minimum amount of cost for a device. If a device has spend less, we won't set a bidadjustment.
  • MINIMUM_CLICKS: Set a minimum number of clicks for a device. If a device has less clicks, we won't set a bidadjustment.
  • KPI: Calculate the bidadjustment based on 'CPA' or 'ROAS'.
  • CAMPAIGN_LABEL: Select campaigns by using a label.
  • MAX_BID: The bidadjustment will not be higher than this. 1.3 = +30% or 2 = +100%.
  • MIN_BID: The bidadjustment will not be lower than this. 0.75 = -25% or 0.5 = -50%.

Scheduling: To be able to collect enough data, we recommend running this script daily or weekly.

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 Head of PPC @ Increase
Nils Rooijmans Water Cooler Topics
Martijn Kraan Freelance PPC Specialist
Bas Baudoin Teamlead SEA @ Happy Leads
How about you? JOIN US!
Sharing Knowledge
Caring

Sharing Knowledge

Adsscripts.com is all about sharing knowledge. In the current market, PPC specialists like to keep their knowledge and experience to themselves. We're convinced that sharing knowledge can ensure that everyone gets better at their work. We want to change this by sharing our knowledge about scripts with everyone.

Do you also want to contribute? We are open to new ideas and feedback on everything you find on Adsscripts.com.

Contact us

Training &
Workshop
Contact us!
Adsscripts Training & Workshop