Magnus Dahlquist | Monday February 12, 2018 | AdWords Scripts

Low Inventory AdWords Script

This “low inventory” script is useful for anyone managing eCommerce clients that do not want land users on low inventory product categories.

This script should work just fine out of the box for most websites. The only element that you might have to adjust to meet your website code is this bit:

regex: /\x3E(\d+[\d\s]*)\x3C\x2Fspan\x3E\Z?\s*SALE\sitems/

The regex in this formula is used to pull the product count for this specific category. This client of ours has a wording that goes something like “354 SALE items” where 354 is the product count in the category.

Other mandadory settings include:

  • minVal = The minimum number of products a category page have to have
  • shouldEnablePausedAds = Allow the script to re-enable previously paused ads if they the landing page reach minimum product threshold
  • recipients = The email addresses that should receive notifications of changes carried out by the script

In addition to this there are also a few optional settings built in:

  • containsArray = Only run the script on campaigns with campaign names that includes certain words or phrases
  • excludesArray = Exclude campaigns with campaign names that includes certain words or phrases
  • labelArray = The script will only run keywords or ads with a certain label
  • campaignStatus = If set then the script will only run on campaigns with a certain status (enabled, paused etc.)
  • adGroupStatus = Same as above but for ad groups
  • status = Same as above but for keywords or ads

Low Inventory AdWords Script Code

function main() {

 // allow script to Enable ads
 var shouldEnablePausedAds = true;

 // Condition pairs to check
 var expressionMask = [
   {
    regex: /\x3E(\d+[\d\s]*)\x3C\x2Fspan\x3E\Z?\s*SALE\sitems/,
     minVal: 2,
     parse: function (rawValue) {
       if (!rawValue || rawValue === ' ')
         return null;
       var resultStr = rawValue.replace(' ', '');
       return +resultStr;
     }
   }
 ];

 var emailLog = [];

 var trimAtQuestionMark = true;

 var type = "ads";
//We usually use ads

 var recipients = ["youremail@email.com"];
 // If set, these addresses will be receive a daily email with a list of bad URLs
 // Add multiple recipients using this format ["a@b.com"] or ["a@b.com","c@d.com","e@g.com"]
 // Leave as [] to skip


 // Optional filtering options
 // Leave as [] to skip
 var containsArray = [];
 // If set, only campaigns with names containing these phrases will be checked.

 var excludesArray = [];
 // If set, campaigns whose names containing any of these phrases will be ignored.

 var labelArray = [];
 // If set, only keywords / ads with these labels will be checked
 // Case sensitive.


 // Status options
 // Choose from ["ENABLED"] if you only want enabled entities
 // ["PAUSED"] if you only want paused entities
 // ["ENABLED","PAUSED"] if you want enabled and paused entities
 var campaignStatus = ["ENABLED","PAUSED"];
 // The status of the campaigns

 var adGroupStatus = ["ENABLED","PAUSED"];
 // The status of the ad groups

 var status = ["ENABLED","PAUSED"];
 // The status of the keywords / ads

 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
 //~~~~~~~~~~~~~~~~~~~ The end of preset options ~~~~~~~~~~~~~~~~~~~~~~~~//

 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//

 var urls = [];
 var bad_urls = [];
 var urlFetchOptions = { muteHttpExceptions: true };
 var countEntities = 0;

 var conditions = [];
 if (containsArray.length > 0) {
   conditions.push(" where the campaign name contains " + containsArray.join(", "));
 }
 if (excludesArray.length > 0) {
   conditions.push(" where the campaign name excludes " + excludesArray.join(", "));
 }
 if (labelArray.length > 0) {
   conditions.push(" where the " + type + " are labelled " + labelArray.join(", "));
 }

 if (containsArray.length === 0) {
   containsArray.push("");
 }

 for (var i = 0; i < containsArray.length; i++) {
   var string = iteratorConstructor(type, containsArray[i], excludesArray, labelArray, status, campaignStatus, adGroupStatus);
   eval(string);
   countEntities += iterator.totalNumEntities();
   excludesArray.push(containsArray[i]);
   while (iterator.hasNext()) {
     var object = iterator.next();
     var url = object.urls().getFinalUrl();

     if (url == null || url == undefined) {
       url = object.getDestinationUrl();
     }

     if (url !== null && url !== undefined) {
       if (trimAtQuestionMark) {
         url = url.split('?')[0];
       }
       if (urls.indexOf(url) === -1) {
         urls.push(url);
         var response = UrlFetchApp.fetch(url, urlFetchOptions);
         var code = response.getContentText();

         for (var pi = 0; pi < expressionMask.length; pi++) {
           var regex = expressionMask[pi].regex;
           var minVal = expressionMask[pi].minVal;
           var parse = expressionMask[pi].parse;

           var result = regex.exec(code);
           if (!result || !result[1])
             continue;

           var numText = result[1];
           var num = parse(numText);

           if (num === null)
             continue;
           
           if (num > minVal) { 
             if (shouldEnablePausedAds && (object.isPaused() || object.getAdGroup().isPaused())) {
               object.getAdGroup().enable();
               object.enable();
               
               var logstr = "adgroup " + object.getAdGroup().getName() + " and ad in it was enabled.";
               Logger.log(logstr); 
               emailLog.push(logstr);
             }
             continue;
           }

           bad_urls.push(url);
           object.pause();
           
           var logstr = "ad in group " + object.getAdGroup().getName() + " was paused.";
           Logger.log(logstr);
           emailLog.push(logstr);

           break;
         }
       }
     }
   }
 }

 if (countEntities == 0) {
   throw "No " + type + " found" + conditions.join("; and");
 }

 Logger.log(countEntities + " " + type + " found" + conditions.join("; and"));
 Logger.log(urls.length + " unique URLs to check.");


 if (bad_urls.length === 0) {
   Logger.log("No bad URLs found.");
 } else {
   Logger.log(bad_urls.length + " found:");
   Logger.log(bad_urls.join("\n"));
 }

 if (recipients.length > 0 && bad_urls.length > 0) {
   var name = AdWordsApp.currentAccount().getName();
   var subject = name + " URL checking";
   var body = 'The following URLs were found to have one of the following phrases in their web page source code. \n"' + '"URLs:\n';
   body += bad_urls.join("\n");
   body += '\nThe following ADS were changed. \n"';
   body += emailLog.join("\n");
   MailApp.sendEmail(recipients.join(","), subject, body);
   Logger.log("Email sent to " + recipients.join(", "));
 }

 function iteratorConstructor(type, containsString, excludesArray, labelArray, status, campaignStatus, adGroupStatus) {

   var string = "var iterator = AdWordsApp." + type + "()";
   if (containsString != "") {
     string = string + ".withCondition('CampaignName CONTAINS_IGNORE_CASE " + '"' + containsString + '"' + "')";
   }
   for (var i = 0; i < excludesArray.length; i++) {
     string = string + ".withCondition('CampaignName DOES_NOT_CONTAIN_IGNORE_CASE " + '"' + excludesArray[i] + '"' + "')";
   }
   if (labelArray.length > 0) {
     string = string + ".withCondition('LabelNames CONTAINS_ANY " + '["' + labelArray.join('","') + '"]' + "')";
   }

   string = string + ".withCondition('Status IN [" + status.join(",") + "]')";
   string = string + ".withCondition('CampaignStatus IN [" + campaignStatus.join(",") + "]')";
   string = string + ".withCondition('AdGroupStatus IN [" + adGroupStatus.join(",") + "]')";
   string = string + ".orderBy('Cost DESC').forDateRange('LAST_30_DAYS')";
   string = string + ".withLimit(50000)";

   string = string + ".get();"

   return string;
 }
}

Happy scripting!

65 Likes

Post a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.