Not consistent Ad approval operation - This doesn't meet editorial guidelines

219 views
Skip to first unread message

Roland Bende

unread,
Mar 14, 2019, 3:44:08 AM3/14/19
to Google Ads Scripts Forum
Help Needed!

I'm using the SAME script for creating expanded text ads via Ads Scripts in TWO different ads accounts with the same test AdCopy data.
I'm experiencing inconsistent Google Ads Ad Approval system behavior.

The PROBLEM:
  • In the first account all ads are created successfully.
  • In the second account some of the ads are not created due to editorial issue: "This doesn't meet editorial guidelines"
  • NONE of the accounts have Trademark use right!

I've attached the script and screenshots from the script history.

Why is this happening???

---

Script Source Code:

/**
 * @name Create Google Ads Expanded Text Ad
 *
 * @overview The script creates an expanded text ad in the given campaign, 
 *     adgroup with the specified elements.
 *     for more details.
 *
 * @author Roland Bende []
 *
 * @version 1.0.2
 *
 * @changelog
 * - version 1.0.2
 *   - Added new functions: My Ad Customizer, Customize Expanded Text Ads with product data
 * - version 1.0.1
 *   - Expension of description legal characters list.
 * - version 1.0.0
 *   - Released initial version.
 */

// OPTIONS

/**
 * Define the campaign id where the adgroup present.
 *
 * @var {string} CAMPAIGN_ID Campaign id
 */
var CAMPAIGN_ID = '1733650936'; //

/**
 * Define the Adgroup name.
 *
 * @var {string} ADGROUP_NAME Adgroup name.
 */
var ADGROUP_NAME = 'Test AdGroup';

/** 
 * Define Expanded text ad templates
 *
 * @var {array <Object>} EXPANDED_TEXT_ADS Expanded text ads templates containg objects(Keys eg.: Headline 1, Desc 2., etc...)
 */
var EXPANDED_TEXT_AD_TEMPLATES = [
  { 
    'ad_template_id': '1',
    'headline1': '{{=product_name}}', // max. length: 30
    'headline2': '{{=product_category}} ingyen szállítással', // max. length: 30
    'headline3': 'Azonnal | Raktárról', // max. length: 30
    'desc1': 'Profi {{=product_category}} a szakértőtől. Részletes termékspecifikáció, hogy a megfelelőt választhasd.', // max. length: 90
    'desc2': 'Magyarországi márkaszerviz hálózattal, gyári garanciával.', // max. length: 90
    'path1': '{{=product_url_path1}}', // max. length: 15
    'path2': '{{=product_url_path2}}', // max. length: 15
    'finalurl': '{{=product_url_final}}'
  },
  { 
    'ad_template_id': '2',
    'headline1': '{{=product_name}}', // max. length: 30
    'headline2': 'Már egymásra találtatok?', // max. length: 30
    'headline3': 'Csak nem tudod, hol vedd meg?', // max. length: 30
    'desc1': 'Vedd meg tőlünk! Most akár 4 órán belül nálad lehet a saját {{=product_name}}.', // max. length: 90
    'desc2': 'Magyarországi márkaszerviz hálózattal, gyári garanciával.', // max. length: 90
    'path1': '{{=product_url_path1}}', // max. length: 15
    'path2': '{{=product_url_path2}}', // max. length: 15
    'finalurl': '{{=product_url_final}}'
  }
];

/**
 * Define products
 *
 * @var {array <Object>}
 */
var PRODUCTS = [
  {
    'name': 'ASUS ROG Zephyrus GM501',
    'category': 'Notebook',
    'url_path1': 'notebook',
    'url_path2': 'asus',
  },
  {
    'name': 'Asus ZenBook Flip S UX370',
    'category': 'Notebook',
    'url_path1': 'notebook',
    'url_path2': 'asus',
  },
  {
    'name': 'HP Spectre x360 13',
    'category': 'Notebook',
    'url_path1': 'notebook',
    'url_path2': 'hp',
  }
];

// FUNCTIONS
function main() {
    Logger.log('Script START!');
    createExpandedTextAdsFromProductData(CAMPAIGN_ID, ADGROUP_NAME, PRODUCTS, EXPANDED_TEXT_AD_TEMPLATES);
    Logger.log('Script END!'); 
}

/**
 * Create Expanded Text Ads From Product Data
 *
 * Dependencies: getGoogleAdsAdGroupByNameAndCampaignId(), customizeExpandedTextAdsWithProductData(), createGoogleAdsExtendedTextAd(), setGoogleAdsScriptTimeout()
 *
 * @param {string} campaign_id
 * @param {string} adgroup_name
 * @param {array <Object>} prodcuts
 * @param {array <Object>} expanded_text_ad_templates
 *
 * @return void
 */
function createExpandedTextAdsFromProductData(campaign_id, adgroup_name, products, expanded_text_ad_templates) {
    var adGroup = getGoogleAdsAdGroupByNameAndCampaignId(adgroup_name, campaign_id);
    var expandedTextAdTemplates = expanded_text_ad_templates;
    for(var i=0; i < products.length; i++) {
        var productAdCopies = [];
        var product = products[i];
        productAdCopies = customizeExpandedTextAdsWithProductData(product, expandedTextAdTemplates);
        productAdCopies.forEach(function(element){
            var ad = createGoogleAdsExtendedTextAd(adGroup, element);
            if(ad !== null) {
                Logger.log('Ad id: ' + ad.getId());
                Logger.log('---');
                Logger.log(ad.getHeadlinePart1() + ' | ' + ad.getHeadlinePart2() + ' | ' + ad.getHeadlinePart3());
                Logger.log(ad.getDescription1());
                Logger.log(ad.getDescription2());
                Logger.log(ad.getPath1() + '/' + ad.getPath2());
                Logger.log(ad.urls().getFinalUrl());
                Logger.log('---');
            }
            setGoogleAdsScriptTimeout(0.2);
        });
    }
    return;
}

/**
 * Customize Expanded Text Ads with product data
 *
 * Dependencies: myAdCustomizer()
 *
 * @param {object} product_data
 * @param {array <Object>} expanded_text_ad_templates
 *
 * @return {array <Object>} customizedAdTemplates
 */
function customizeExpandedTextAdsWithProductData(product_data, expanded_text_ad_templates) {
  var customizedAdTemplates = [];
  var productData = product_data;
  var adTemplates = expanded_text_ad_templates;
  var adTemplatesLength = adTemplates.length;
  for(var index=0; index < adTemplatesLength; index++) {
    var adTemplate = adTemplates[index];
    var customizedAdTemplate = myAdCustomizer(adTemplate, '{{=product_name}}', productData['name']);
    customizedAdTemplate = myAdCustomizer(customizedAdTemplate, '{{=product_category}}', productData['category']);
    customizedAdTemplate = myAdCustomizer(customizedAdTemplate, '{{=product_url_path1}}', productData['url_path1']);
    customizedAdTemplate = myAdCustomizer(customizedAdTemplate, '{{=product_url_path2}}', productData['url_path2']);
    customizedAdTemplate = myAdCustomizer(customizedAdTemplate, '{{=product_url_final}}', productData['url_final']);
    customizedAdTemplates.push(customizedAdTemplate);
  }
  return customizedAdTemplates;
}

/**
 * My Ad Customizer - Replaces given variables with the given values in the given Ad object.
 *
 * Dependecies: stringReplace()
 *
 * @param {object <String>} ad_elements
 * @param {string} my_ad_customizer_variable
 * @param {string} my_ad_customizer_value
 *
 * @return {object <String>} adElements
 */ 
function myAdCustomizer(ad_elements, my_ad_customizer_variable, my_ad_customizer_value) {
    var adElements = ad_elements;
    var newAdElements = {};
    for(var key in adElements) {
        if(adElements.hasOwnProperty(key)) {
            newAdElements[key] = stringReplace(adElements[key], my_ad_customizer_variable, my_ad_customizer_value);
        }
    }
    return newAdElements;
}

/**
 * Replace string part in string with the given string using regular expression.
 *
 * Dependencies: isVariableNull(), isVariableString(), isStringEmpty()
 *
 * @param {string} my_string The string to operate on.
 * @param {string} replace_what The string part which need to be replaced.
 * @param {string} replace_to The new string part which 
 *
 * @return {string}
 */
function stringReplace(my_string, replace_what, replace_to) {
    if(isVariableNull(my_string) === false &&  isVariableString(my_string) === true && isStringEmpty(my_string) === false) {
        var my_regex = new RegExp(replace_what, 'g');
        return my_string.replace(my_regex, replace_to);    
    } else {
        return my_string;
    }
}

/**
 * Check variable is string type variable.
 *
 * @param {string} my_var The variable to check.
 *
 * @return {boolean}
 */
function isVariableString(my_var) {
    if(typeof my_var === "string") {
        return true;
    } else {
        Logger.log('Error: Given param ('+ my_var +') is not a string!');
        return false;
    }
}

/**
 * Check string is empty or not.
 *
 * @param {string} my_string The string to check.
 *
 * @return {boolean}
 */
function isStringEmpty(my_string) {
    if(isVariableString(my_string) === false) {
        return false;
    }
    return my_string.length === 0 ? true : false;
}

/**
 * Check variable is empty or not.
 *
 * @param {string} my_var The variable to check.
 *
 * @return {boolean}
 */
function isVariableNull(my_var) {
    return my_var === null ? true : false;
}

/**
 * Get Google Ads AdGroup by name and it's Campaign id.
 *
 * 
 * @param {string} my_adgroup_name AdGroup name selector.
 * @param {number} my_camapign_id Campaign id selector. Basically Not neccessary but recommended because of permormance reasons.
 *
 * @return {?AdGroup} my_selected_adgroup Returns selected AdGroup. If error occured it will be null.
 */
function getGoogleAdsAdGroupByNameAndCampaignId(my_adgroup_name, my_campaign_id) {
    var my_selected_adgroup = null;
    try {
        my_selected_adgroup = AdsApp.adGroups()
            .withCondition('Name = "'+ my_adgroup_name +'"')
            .withCondition('CampaignId="'+ my_campaign_id +'"')
            .get()
            .next();
    } catch(e) {
        Logger.log('Warning: No AdGroup found with the name ' + my_adgroup_name + ' in given campaign(id:'+ my_campaign_id +')!');
    }
    return my_selected_adgroup;
}

/**
 * Create Google Ads extended text ad.
 *
 * 
 * @param {!AdGroup} my_adgroup Destination AdGroup entity.
 * @param {object} my_expanded_text_ad_settings
 *
 * @return {!ExpandedTextAd || null} Normally returns a valid Expanded Text Ad entity. If error occured it returns null as value! 
 */
function createGoogleAdsExtendedTextAd(my_adgroup, my_expanded_text_ad_settings) {
    var ad = null;
    var adGroup = my_adgroup;
    var adSettings = my_expanded_text_ad_settings;
    var adHeadline1 = createGoogleAdsExtendedTextAdHeadlineTextFromString(adSettings['headline1']);
    var adHeadline2 = createGoogleAdsExtendedTextAdHeadlineTextFromString(adSettings['headline2']);
    var adHeadline3 = createGoogleAdsExtendedTextAdHeadlineTextFromString(adSettings['headline3']);
    var adDesc1 = createGoogleAdsExtendedTextAdDescriptionTextFromString(adSettings['desc1']);
    var adDesc2 = createGoogleAdsExtendedTextAdDescriptionTextFromString(adSettings['desc2']);
    var adPath1 = createGoogleAdsExtendedTextAdPathTextFromString(adSettings['path1']);
    var adPath2 = createGoogleAdsExtendedTextAdPathTextFromString(adSettings['path2']);
    var adFinalUrl = createGoogleAdsExtendedTextAdFinalUrlTextFromString(adSettings['finalurl']);
    if(!adHeadline1 || !adHeadline2 || !adDesc1 || !adFinalUrl) {
        Logger.log("Error: There was an error when tried to create Expanded Text Ad!");
        Logger.log("Required parameter(s) missing!");
        return null;
    }
    try {
       var adOperation = adGroup.newAd().expandedTextAdBuilder()
        .withHeadlinePart1(adHeadline1)
        .withHeadlinePart2(adHeadline2)
        .withHeadlinePart3(adHeadline3)
        .withDescription1(adDesc1)
        .withDescription2(adDesc2)
        .withPath1(adPath1)
        .withPath2(adPath2)
        .withFinalUrl(adFinalUrl)
        .build();
        ad = adOperation.getResult();
    } catch(e) {
        Logger.log('Error: There was an error when tried to create Expanded Text Ad!');
        Logger.log(e);
    }
    return ad;
}

/** 
 * Create Google Ads Expanded Text Ad Headline from given string.
 *
 * Dependecies: isStringlongerThen(), sanitizeGoogleAdsExpandedTextAdHeadlineText()
 *
 *
 * @param {string} my_headline The headline string.
 * 
 * @return {string || null} Normally returns a valid Expanded Text Ad Headline. If error occured it returns null as value! 
 */
function createGoogleAdsExtendedTextAdHeadlineTextFromString(my_headline) {
    var sanitized_headline_text = sanitizeGoogleAdsExpandedTextAdHeadlineText(my_headline);
    var expanded_text_ad_headline_max_char_limit = 30;
    if(isStringlongerThen(sanitized_headline_text, expanded_text_ad_headline_max_char_limit)) {
        Logger.log("Error: Can't create AdCopy Headline! Given string ("+ sanitized_headline_text +") is longer then "+expanded_text_ad_headline_max_char_limit+" chars!");
        return null;
    }
    return sanitized_headline_text;
}

/** 
 * Create Google Ads Expanded Text Ad Description from given string.
 *
 * Dependecies: isStringlongerThen(), sanitizeGoogleAdsExpandedTextAdDescriptionText()
 *
 *
 * @param {string} my_desc The description string.
 * 
 * @return {string || null} Normally returns a valid Expanded Text Ad Description. If error occured it returns null as value! 
 */
function createGoogleAdsExtendedTextAdDescriptionTextFromString(my_desc) {
    var sanitized_description_text = sanitizeGoogleAdsExpandedTextAdDescriptionText(my_desc); // ToDo Desc. sanitize function
    var expanded_text_ad_description_max_char_limit = 90;
    if(isStringlongerThen(sanitized_description_text, expanded_text_ad_description_max_char_limit)) {
        Logger.log("Error: Can't create AdCopy Description! Given string ("+ sanitized_description_text +") is longer then "+expanded_text_ad_description_max_char_limit+" chars!");
        return null;
    }
    return sanitized_description_text;
}

/** 
 * Create Google Ads Expanded Text Ad Path from given string.
 *
 * Dependecies: isStringlongerThen(), sanitizeGoogleAdsExpandedTextAdPathText()
 *
 *
 * @param {string} my_path The path string.
 * 
 * @return {string || null} Normally returns a valid Expanded Text Ad Path. If error occured it returns null as value! 
 */
function createGoogleAdsExtendedTextAdPathTextFromString(my_path) {
    var sanitized_path_text = sanitizeGoogleAdsExpandedTextAdPathText(my_path);
    var expanded_text_ad_path_max_char_limit = 15;
    if(isStringlongerThen(sanitized_path_text, expanded_text_ad_path_max_char_limit)) {
        Logger.log("Error: Can't create AdCopy Path! Given string ("+ sanitized_path_text +") is longer then "+expanded_text_ad_path_max_char_limit+" chars!");
        return null;
    }
    return sanitized_path_text;
}

/** 
 * Create Google Ads Expanded Text Ad Final URL from given string.
 *
 * Dependecies: isStringlongerThen(), sanitizeGoogleAdsExpandedTextAdFinalUrlText()
 *
 *
 * @param {string} my_final_url The final url string.
 * 
 * @return {string || null} Normally returns a valid Expanded Text Final Url. If error occured it returns null as value! 
 */
function createGoogleAdsExtendedTextAdFinalUrlTextFromString(my_final_url) {
    var sanitized_path_text = sanitizeGoogleAdsExpandedTextAdFinalUrlText(my_final_url);
    return sanitized_path_text;
}

/** 
 * Remove illegal characters from given Google Ads Expanded Text Ad Headline text.
 *
 * @param {string} my_headline_text The headline string to sanitize.
 * 
 * @return {string} sanitized_text Sanitized headline string. Illegal chars are removed!
 */
function sanitizeGoogleAdsExpandedTextAdHeadlineText(my_headline_text) {
    var sanitized_text = my_headline_text.replace(/[^0-9a-záéíóöőüűú+'"_-]/gi, " ");
    sanitized_text = sanitized_text.replace(/  +/g, " ");
    sanitized_text = sanitized_text.trim();
    return sanitized_text;
}

/** 
 * Remove illegal characters from given Google Ads Expanded Text Ad Description text.
 *
 * @param {string} my_desc_text The description string to sanitize.
 * 
 * @return {string} sanitized_text Sanitized description string. Illegal chars are removed!
 */
function sanitizeGoogleAdsExpandedTextAdDescriptionText(my_desc_text) {
    var sanitized_text = my_desc_text.replace(/[^0-9a-záéíóöőüűú+'".,!?()[]_-]/gi, " ");
    sanitized_text = sanitized_text.replace(/  +/g, " ");
    sanitized_text = sanitized_text.trim();
    return sanitized_text;
}

/** 
 * Remove illegal characters from given Google Ads Expanded Text Ad Path.
 *
 * @param {string} my_path_text The path string to sanitize.
 * 
 * @return {string} sanitized_text Sanitized path string. Illegal chars are removed!
 */
function sanitizeGoogleAdsExpandedTextAdPathText(my_path_text) {
    var sanitized_text = my_path_text.toLowerCase(); // Only lowercase letters
    sanitized_text = sanitized_text.replace(/[^0-9a-záéíóöőüűú_-]/g, " ");
    sanitized_text = sanitized_text.replace(/  +/g, " ");
    sanitized_text = sanitized_text.trim();
    return sanitized_text;
}

/** 
 * Remove illegal characters from given Google Ads Expanded Text Ad Final Url.
 *
 * For more info visit: https://www.ietf.org/rfc/rfc3986.txt
 *
 * @param {string} my_final_url_text The final url string to sanitize.
 * 
 * @return {string} sanitized_text Sanitized path string. Illegal chars are removed!
 */
function sanitizeGoogleAdsExpandedTextAdFinalUrlText(my_final_url_text) {
    var sanitized_text = my_final_url_text.toLowerCase();
    sanitized_text = sanitized_text.trim();
    return sanitized_text;
}

/**
 * Check string lenght is it is longer than the given limit.
 *
 * 
 * @param {string} my_string The string to check.
 * @param {number} my_limit The maximum length of the string.
 *
 * @return {boolean} If incorrect param type was given then it return false with an error log.
 */
function isStringlongerThen(my_string, my_limit) {
    if(isVariabelString(my_string) === false) {
        return false;
    }
    if(isVariabelNumber(my_limit) === false) {
        return false;
    }
    if(my_string.length > my_limit) {
        return true;
    }
    return false;
}

/**
 * Check variable is string type variable.
 *
 * @param {string} my_var The variable to check.
 *
 * @return {boolean}
 */
function isVariabelString(my_var) {
    if(typeof my_var === "string") {
        return true;
    } else {
        Logger.log('Error: Given param ('+ my_var +') is not a string!');
        return false;
    }
}

/**
 * Check variable is number type.
 *
 * @param {string} my_var The variable to check.
 *
 * @return {boolean}
 */
function isVariabelNumber(my_var) {
    if(typeof my_var === "number") {
        return true;
    } else {
        Logger.log('Error: Given param ('+ my_var +') is not a number!');
        return false;
    }
}

/**
 * Pretty print javascript object as a string for debug purpose.
 *
 * @param {object} value The javascript array to pretty print. 
 * 
 * @return {string} Human-readable object.
 */
function prettyPrintObject(value) {
    return JSON.stringify(value, null, '\t');
}

/**
 *
 * Set timeout for Google Ads Script
 *
 *
 * @param {integer} Timeout multiplier.
 *
 */
function setGoogleAdsScriptTimeout(multiplier) {
  var timeout_multiplier = multiplier;
  Utilities.sleep(timeout_multiplier * 3 * 1000);
}


01 - successful.png
02 - errors.png

googleadsscrip...@google.com

unread,
Mar 14, 2019, 7:06:54 AM3/14/19
to adwords...@googlegroups.com, Google Ads Scripts Forum
Hi Roland,

Unfortunately, our team doesn't provide support for third-party script related issues/concerns. I suggest that you may try to reach out the author of the script so they can provide suggestions on how to resolve your issue.

Additionally, its seems that the ads that you are trying to create via Google Ads Scripts violated some rules listed in this link and this issue is more specific at product level. With this, you may try to post your issue to Google Ads Community Forum and please specify the information of the ads that you are trying to create so they can further check and they can provide supports/suggestions.

Regards,
Ejay
Google Ads Scripts Team
=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
Also find us on our blog and discussion group:
    http://googleadsdeveloper.blogspot.com/search/label/adwords_scripts
    https://developers.google.com/google-ads/scripts/community/
=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
Reply all
Reply to author
Forward
0 new messages