To gain more insight into the performance of your visitors and customers, I always add target groups to campaigns. This allows you to make bid adjustments based on the performance of these target groups. Depending on the customer, we sometimes added 100 different target groups to a campaign. Instead of tracking these target groups every week and changing bid adjustments, we have automated this process.

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

Settings

In the script you can only adjust the following things:

  • LOG: Specify whether the script should report the intermediate steps by adjusting the value to 'true'.
  • DATE_RANGE: The script looks at the conversions and costs over this period.
  • MINIMUM_CONVERSIONS: Set a minimum number of conversions for an audience. If an audience has less conversions, we won't set a bidadjustment.
  • MINIMUM_COST: Set a minimum amount of cost for an audience. If an audience has spend less, we won't set a bidadjustment.
  • CAMPAIGN_LABEL: Select campaigns by using a label.
  • MAX_BID: Bidadjustment won't be higher then set here.
  • MIN_BID: Bidadjustment won't be lower then set here.

Scheduling: To generate sufficient data we advise to run the script once a week.

Script
// Copyright 2019. Increase BV. All Rights Reserved.
//
// Created By: Tibbe van Asten
// for Increase B.V.
// 
// Created: 01-03-2019
// Last update: 17-05-2019
//
// ABOUT THE SCRIPT
// With this script we adjust the biddings for audiences in 
// active campaigns.
//
////////////////////////////////////////////////////////////////////

var config = {

  LOG : true,
  DATE_RANGE : "LAST_30_DAYS",
  
  // Optional: Use minimum conversions and/or cost to select audiences
  // Leave empty to skip
  MINIMUM_CONVERIONS : "",
  MINIMUM_COST : "",
  
  // 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() {
  
  // We start by getting all active campaigns
  var campaignIterator = campaignSelector();
  
  while(campaignIterator.hasNext()) {
  	var campaign = campaignIterator.next();    
    var campaignCpa = campaign.getStatsFor(config.DATE_RANGE).getCost() / campaign.getStatsFor(config.DATE_RANGE).getConversions();
    
    	if(config.LOG === true) {
        	Logger.log("Campagne: " + campaign.getName()); 
          	Logger.log("Campagne CPA: " + campaignCpa); 
          	Logger.log("-----");
        }
    
    // Within the campaign, we will check for audiences
    var audienceIterator = audienceSelector(campaign);

    while(audienceIterator.hasNext()) {
      var audience = audienceIterator.next();

      var audienceCpa = audience.getStatsFor(config.DATE_RANGE).getCost() / audience.getStatsFor(config.DATE_RANGE).getConversions();
      var newAudienceBid = campaignCpa / audienceCpa;

        if(config.LOG === true && isNaN(newAudienceBid) === false && isFinite(audienceCpa) === true && audience.isEnabled() === true && isNaN(audienceCpa) === false) {
          Logger.log("Audience: " + audience.getName()); 
          Logger.log("Audience CPA: " + audienceCpa);
          Logger.log("Suggested Audience bid: " + newAudienceBid);
        }         	
      
      // If there is an audience CPA, we will place a bidadjustment
      if(isNaN(newAudienceBid) === false && isFinite(audienceCpa) === true && audience.bidding().getBidModifier() !== newAudienceBid && audience.isEnabled() === true) {
        	if(newAudienceBid < config.MIN_BID) { newAudienceBid = config.MIN_BID; }
        	if(newAudienceBid > config.MAX_BID) { newAudienceBid = config.MAX_BID; }
        
        	audience.bidding().setBidModifier(newAudienceBid);        
        	Logger.log("Bidadjustment " + audience.getName() + " changed to " + newAudienceBid);
        	Logger.log(" ");
      }

    } // audience iterator    
    
    if(config.LOG === true){
      Logger.log(" ");
    }
    
  } // campaign iterator
  
} // function main()

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

function campaignSelector(){

  if(config.CAMPAIGN_LABEL != ""){
    var campaignSelector = AdsApp
      .campaigns()
      .withCondition("Status = ENABLED")
      .withCondition("LabelNames CONTAINS_ANY ['" + config.CAMPAIGN_LABEL + "']")
      .get();
  } else{
    var campaignSelector = AdsApp
      .campaigns()
      .withCondition("Status = ENABLED")
      .get();
  }
  
  return campaignSelector;
  
} // function campaignSelector()

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

function audienceSelector(campaign){

  // When no limitations are set
  if(config.MINIMUM_CONVERIONS == "" && config.MINIMUM_COST == ""){
    var audienceSelector = campaign
      .targeting()
      .audiences()
      .get();
  }
  
  // When a converions minimum is set
  if(config.MINIMUM_CONVERIONS != "" && config.MINIMUM_COST == ""){
    var audienceSelector = campaign
      .targeting()
      .audiences()
      .withCondition("Conversions > " + config.MINIMUM_CONVERIONS)
      .forDateRange(config.DATE_RANGE)
      .get();
  }
  
  // When a cost minimum is set
  if(config.MINIMUM_CONVERIONS == "" && config.MINIMUM_COST != ""){
    var audienceSelector = campaign
      .targeting()
      .audiences()
      .withCondition("Cost > " + config.MINIMUM_COST)
      .forDateRange(config.DATE_RANGE)
      .get();
  }
  
  // When cost and conversion minima are set
  if(config.MINIMUM_CONVERIONS != "" && config.MINIMUM_COST != ""){
    var audienceSelector = campaign
      .targeting()
      .audiences()
      .withCondition("Conversions > " + config.MINIMUM_CONVERSIONS)
      .withCondition("Cost > " + config.MINIMUM_COST)
      .forDateRange(config.DATE_RANGE)
      .get();
  }
  
  return audienceSelector;
  
} // function audienceSelector()

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

Nils Rooijmans
Google Ads Scripter
Water Cooler Topics
Nils Rooijmans, Google Ads Scripter
Bas Baudoin
Teamleader PPC
Happy Leads
Bas Baudoin, Teamleader PPC
Martijn Kraan
Freelance PPC Specialist
Brightstep
Martijn Kraan, Freelance PPC Specialist
Tibbe van Asten
PPC Specialist
Founder Adsscripts
Tibbe van Asten, Senior PPC Automation Specialist