Zoektermen toevoegen als zoekwoorden

Zoektermen toevoegen als zoekwoord wanneer deze goed presteren. Een makkelijke manier om een account uit te breiden.

Start nu!
Zoektermen toevoegen als zoekwoorden
Zoektermen Get started!

Bij het optimaliseren van campagnes in Google Ads wordt vaak het zoekterm rapport erbij gepakt. Op basis van prestaties en boerenverstand worden daar zoektermen uitgesloten, maar vaak staan er ook zoekwoorden tussen die je kan toevoegen aan je advertentiegroep. Daarmee krijg je meer grip op de prestaties van deze specifieke zoektermen, omdat je biedingen kan aanpassen naar wens.

Met onderstaand script kan je dit proces automatiseren. Op basis van de prestaties van een zoekwoord, wordt deze automatisch toegevoegd wanneer de prestaties goed zijn. De minimale vereisten voor vertoningen, CTR en conversies stel je zelf in. Hierdoor heb je controle over de zoekwoorden die het script daadwerkelijk aan de advertentiegroep gaat toevoegen.

Daarbij controleert het script ook of het exact zoekwoord dat het wil gaan toevoegen, niet al ergens anders in het account aanwezig is. Hierdoor wordt de structuur van het account niet aangetast.

Instellingen

  • IMPRESSIONS_THRESHOLD: Stel het minimaal aantal vertoningen in dat een zoekterm moet hebben.
  • CONVERSIONS_THRESHOLD: Stel het minimale aantal conversies in dat een zoekterm moet hebben.
  • CTR_THRESHOLD: Alleen zoektermen opnemen die aan deze minimale CTR voldoen.
  • DATE_RANGE: Aangeven over welke periode het script bovenstaande vereisten moet controleren. Verander het aantal in last_n_days().
  • LOG_LEVEL: Wanneer er iets mis gaat, kan je deze waarde aanpassen om de uitkomst in het logbestand te bekijken.

Frequentie: Afhankelijk van de hoeveelheid verkeer in een account, adviseren we dit script maximaal 1x per dag te draaien.

The script
//
// Auto-optimize Search Terms
// Created by: Remko van der Zwaag & pdds
// remkovanderzwaag.nl & pdds.nl
//
// Based on a Google example script: http://goo.gl/aunUKV
// Updated by Tibbe van Asten
//
// Last update: 14-01-2020
//
////////////////////////////////////////////////////////////////////

var config = {

  // Minimum number of impressions to consider "enough data"
  IMPRESSIONS_THRESHOLD : 100,

  // Before new keywords are eligible to be added to the ad group,
  // the search term metrics need to exceed the Conversion OR CTR threshold

  // Minimal number of conversions
  CONVERSIONS_THRESHOLD : 2,

  // Minimal keyword CTR
  CTR_THRESHOLD : 20, // Use dots for decimals, eg 0.5

  // Maximum cost/conversions. Leave empty to ignore
  CPA_THRESHOLD : , // Use dots for decimals, eg 0.5

  // The date range to investigate for potential keywords
  // Formatted as an AWQL DateRange, so you can use this helper,
  // one of the enumerations ('LAST_7_DAYS', 'YESTERDAY', etc),
  // or a manual range like '20140101,20140529'
  DATE_RANGE : last_n_days(90),

  // The script doesn't do much logging in the current version. Set to 'debug' to debug.
  LOG_LEVEL : 'error'

}

////////////////////////////////////////////////////////////////////
// Please don't touch below this line

function main() {
  getAllKeywords();

  var negativeKeywords = {};
  var positiveKeywords = {};
  var allAdGroupIds = {};

  var report = AdsApp.report(
    "SELECT Query,Clicks,Cost,Ctr,ConversionRate,CostPerConversion,Conversions,CampaignId,AdGroupId " +
    " FROM SEARCH_QUERY_PERFORMANCE_REPORT " +
    " WHERE Impressions >= " + config.IMPRESSIONS_THRESHOLD +
    " AND AdGroupStatus = ENABLED " +
    " AND CampaignStatus = ENABLED " +
    " DURING " + config.DATE_RANGE);
  var rows = report.rows();

  // Iterate through search query and decide whether to
  // add them as positive or negative keywords (or ignore).
  while (rows.hasNext()) {
    var row = rows.next();
    // If query exists as keyword, we don't need to process; report and move on
    if (keywordExists(row['Query'])) {
      debug([row['Query'], 'exists'].join(': '));
      continue;
    }
    debug([row['Query'], 'doesn\'t exist'].join(': '));
    // If the keyword doesn't exist, check if query meets criteria for
    // for addition as exact keyword

    // Currently, either needs to beat the CTR_THRESHOLD or
    // the CONVERSIONS_THRESHOLD
    if (parseFloat(row['Ctr']) >= config.CTR_THRESHOLD ||
    	parseInt(row['Conversions']) >= config.CONVERSIONS_THRESHOLD) {
      
      // When CPA_THRESHOLD is set, Cost per conversion must be lower than the CPA_THRESHOLD
      if(config.CPA_THRESHOLD != ""){
        if(parseFloat(row['CostPerConversion'] < config.CPA_THRESHOLD){
          // Save query as a keyword to be added to this adGroup
          addToMultiMap(positiveKeywords, row['AdGroupId'], row['Query']);
          allAdGroupIds[row['AdGroupId']] = true;
        }
      } 

      if(config.CPA_THRESHOLD == ""){
        // Save query as a keyword to be added to this adGroup
        addToMultiMap(positiveKeywords, row['AdGroupId'], row['Query']);
        allAdGroupIds[row['AdGroupId']] = true;
      }
      
    }
  } // rowIterator

  // Copy all the adGroupIds from the object into an array to allow bulkprocessing of groups
  var adGroupIdList = [];
  for (var adGroupId in allAdGroupIds) {
    adGroupIdList.push(adGroupId);
  }

  // Fetch all touched adGroups and process relevant keywords
  var adGroups = AdsApp.adGroups().withIds(adGroupIdList).get();
  while (adGroups.hasNext()) {
    var adGroup = adGroups.next();
    // Add negative keywords that were saved to be added to the adGroup
    // This version of the script doesn't mark keywords as negative,
    // but the plumbing is there if you want to give it a try
    if (negativeKeywords[adGroup.getId()]) {
      for (var i = 0; i < negativeKeywords[adGroup.getId()].length; i++) {
        adGroup.createNegativeKeyword('[' + negativeKeywords[adGroup.getId()][i] + ']');
      }
    }
    // Add positive keywords that were saved to be added to the adGroup
    if (positiveKeywords[adGroup.getId()]) {
      for (var i = 0; i < positiveKeywords[adGroup.getId()].length; i++) {
        adGroup.createKeyword('[' + positiveKeywords[adGroup.getId()][i] + ']');
      }
    }
  } // adGroupIterator
} // function main()

// All the exact keywords in the account
var allKeywordsMap = {};

////////////////////////////////////////////////////////////////////
// Fill the allKeywordsMap with all keywords
function getAllKeywords() {
  var options = { includeZeroImpressions : true }; // Include keywords that aren't used
  // AWQL query to find all keywords in the account
  var query = "SELECT Criteria, KeywordMatchType FROM KEYWORDS_PERFORMANCE_REPORT WHERE KeywordMatchType = EXACT DURING LAST_7_DAYS";
  var reportIter = AdsApp.report(query, options).rows();
  while(reportIter.hasNext()) {
    var row = reportIter.next();
    debug("Exact keyword: '" + row.Criteria + "'");
    // Save as key, for easy lookup
    allKeywordsMap[row.Criteria.toLowerCase()] = true;
  }
  return allKeywordsMap;
} // function getAllKeywords()

////////////////////////////////////////////////////////////////////
// Check if keyword exists, only works if getAllKeywords has been run.
function keywordExists(keyword) {
  return (allKeywordsMap[keyword.toLowerCase()] !== undefined);
} // function keywordExists()


////////////////////////////////////////////////////////////////////
function addToMultiMap(map, key, value) {
  if (!map[key]) {
    map[key] = [];
  }
  map[key].push(value);
} // function addToMultiMap()

////////////////////////////////////////////////////////////////////
// Convenience function to generate a date range based on the current date.
function last_n_days(n) {
	var	from = new Date(),
		to = new Date();
	to.setUTCDate(from.getUTCDate() - n);
	return google_date_range(from, to);
} // function last_n_days()

////////////////////////////////////////////////////////////////////
// Convenience function to generate a google formatted date range based on js Date objects
function google_date_range(from, to) {
	function google_format(date) {
		var date_array = [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()];
		if (date_array[1] < 10) date_array[1] = '0' + date_array[1];
		if (date_array[2] < 10) date_array[2] = '0' + date_array[2];
		return date_array.join('');
	}
	var inverse = (from > to);
	from = google_format(from);
	to = google_format(to);
	var result = [from, to];
	if (inverse) {
		result = [to, from];
	}
	return result.join(',');
} // function google_date_range()

////////////////////////////////////////////////////////////////////
// Some functions to help with logging - gracefully borrowed from http://www.freeadwordsscripts.com
var LOG_LEVELS = { 'error':1, 'warn':2, 'info':3, 'debug':4 };
function error(msg) { if(LOG_LEVELS['error'] <= LOG_LEVELS[LOG_LEVEL]) { log('ERROR',msg); } }
function warn(msg)  { if(LOG_LEVELS['warn']  <= LOG_LEVELS[LOG_LEVEL]) { log('WARN' ,msg); } }
function info(msg)  { if(LOG_LEVELS['info']  <= LOG_LEVELS[LOG_LEVEL]) { log('INFO' ,msg); } }
function debug(msg) { if(LOG_LEVELS['debug'] <= LOG_LEVELS[LOG_LEVEL]) { log('DEBUG',msg); } }
function log(type,msg) { Logger.log(type + ' - ' + msg); }
Show whole script!
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

Kennis delen

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