Skip to content

Instantly share code, notes, and snippets.

  • Star 9 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
What would you like to do?
Add placement exclusions if a Google Ads placement name contains a character in a disallowed Unicode script
* Placement Exclusion
* @version: 1.0
* @author: Naman Jindal (Optmyzr)
* -------------------------------
* Visit for PPC management tools and scripts
* including Rule-based automations, Reports, Audits, Team workflows,
* and optimization suggestions.
* -------------------------------
* Note Google limits placement (content) exclusions that may
* cause errors in the script. Read about allowed exclusions by campaign type at
* In order to support the exclusion of YT placements, this script uses a workaround and
* creates the exclusions via bulk edits. You will need to review your bulk edits
* in the Google Ads interface after running the script.
Array of Scripts that are supported.
Placement will be excluded if a character which does not belong to any of the scripts in the array is found in its name
Scripts Supported:
'Latin', 'Greek', 'Coptic', 'Cyrillic', 'Armenian', 'Hebrew', 'Arabic', 'Syriac',
'Thaana', 'Devanagari', 'Bengali', 'Gurmukhi', 'Gujarati', 'Oriya', 'Tamil', 'Telugu',
'Kannada', 'Malayalam', 'Sinhala', 'Thai', 'Lao', 'Tibetan', 'Myanmar', 'Georgian',
'Hangul_Jamo', 'Ethiopic', 'Cherokee', 'Ogham', 'Runic', 'Tagalog', 'Hanunoo', 'Buhid',
'Tagbanwa', 'Khmer', 'Mongolian', 'Limbu', 'Tai_Le'
//Name of the exclusion list. If the list does not exist, script will create it in the account
// Maximum number of placements to be added to the exclusion list
// Lookback period for Clicks
var LAST_N_DAYS = 90;
// Do not edit below this line
function main() {
var listIter = AdsApp.excludedPlacementLists().withCondition(" = '"+EXCLUSION_LIST_NAME+"'").get();
if(!listIter.hasNext()) {
var list = AdsApp.excludedPlacementLists().withCondition(" = '"+EXCLUSION_LIST_NAME+"'").get().next();
var existingPlacements = {};
var placements = list.excludedPlacements().get();
while(placements.hasNext()) {
var pl =;
existingPlacements[pl.getUrl()] = 1;
var query = [
'SELECT, shared_criterion.keyword.text, shared_criterion.youtube_video.video_id,',
'shared_criterion.youtube_channel.channel_id, shared_criterion.placement.url,',
'FROM shared_criterion WHERE'+list.getId()
].join(' ');
var rows =;
while(rows.hasNext()) {
var row =;
if(row['']) {
existingPlacements[row['']] = 1;
if(row['shared_criterion.youtube_video.video_id']) {
existingPlacements['' + row['shared_criterion.youtube_video.video_id']] = 1;
if(row['shared_criterion.youtube_channel.channel_id']) {
existingPlacements['' + row['shared_criterion.youtube_channel.channel_id']] = 1;
if(row['shared_criterion.placement.url']) {
existingPlacements[row['shared_criterion.placement.url']] = 1;
var existingPlacementsCount = Object.keys(existingPlacements).length;
var PLACEMENTS_LEFT = MAX_PLACEMENTS - existingPlacementsCount;
Logger.log('Maximum # placements already added. Exiting!');
var START = getGoogleAdsFormattedDate(LAST_N_DAYS, 'yyyy-MM-dd'),
END = getGoogleAdsFormattedDate(0, 'yyyy-MM-dd');
var videoList = [], toAdd = [];
var query = [
'SELECT detail_placement_view.display_name, detail_placement_view.placement_type,',
'detail_placement_view.resource_name, detail_placement_view.placement,',
'detail_placement_view.group_placement_target_url, detail_placement_view.target_url,',
'metrics.clicks FROM detail_placement_view',
'WHERE between "'+START+'" AND "'+END+'"',
//'and detail_placement_view.placement_type != YOUTUBE_VIDEO',
'ORDER BY metrics.clicks DESC'
].join(' ');
var rows =;
while(rows.hasNext()) {
var row =;
var placementName = row['detail_placement_view.display_name'];
var placementUrl = row['detail_placement_view.target_url'];
if(!placementUrl) { continue; }
var placementUrlCheck = '';
if(placementUrl.indexOf('') > -1) {
placementUrlCheck = 'mobileapp::1-' + placementUrl.split('app/id')[1];
} else if(placementUrl.indexOf('') > -1) {
placementUrlCheck = 'mobileapp::2-' + placementUrl.split('id=')[1];
} else {
placementUrlCheck = placementUrl;
if(existingPlacements[placementUrlCheck]) { continue; }
var isAllowed = checkForAllowedScripts(placementName);
if(!isAllowed) {
existingPlacements[placementUrl] = 1;
if(PLACEMENTS_LEFT < 1) { break; }
var columnHeads = [
'Action', 'Placement Exclusion List Name', 'Placement url'
var upload = AdWordsApp.bulkUploads().newCsvUpload(columnHeads);
for(var z in toAdd) {
'Action': 'Add',
'Placement Exclusion List Name': EXCLUSION_LIST_NAME,
'Placement url': toAdd[z]
Logger.log('Number of new placements to add: ' + toAdd.length);
Logger.log('Placements were added via Bulk upload. Please check Uploads under Scripts section for logs.');
function checkForAllowedScripts(str) {
var unicodeMap = {
'Latin': /[\u0000-\u007F]/,
'Greek': /[\u0370-\u03FF]/,
'Coptic': /[\u0370-\u03FF]/,
'Cyrillic': /[\u0400-\u04FF]/,
'Armenian': /[\u0530-\u058F]/,
'Hebrew': /[\u0590-\u05FF]/,
'Arabic': /[\u0600-\u06FF]/,
'Syriac': /[\u0700-\u074F]/,
'Thaana': /[\u0780-\u07BF]/,
'Devanagari': /[\u0900-\u097F]/,
'Bengali': /[\u0980-\u09FF]/,
'Gurmukhi': /[\u0A00-\u0A7F]/,
'Gujarati': /[\u0A80-\u0AFF]/,
'Oriya': /[\u0B00-\u0B7F]/,
'Tamil': /[\u0B80-\u0BFF]/,
'Telugu': /[\u0C00-\u0C7F]/,
'Kannada': /[\u0C80-\u0CFF]/,
'Malayalam': /[\u0D00-\u0D7F]/,
'Sinhala': /[\u0D80-\u0DFF]/,
'Thai': /[\u0E00-\u0E7F]/,
'Lao': /[\u0E80-\u0EFF]/,
'Tibetan': /[\u0F00-\u0FFF]/,
'Myanmar': /[\u1000-\u109F]/,
'Georgian': /[\u10A0-\u10FF]/,
'Hangul_Jamo': /[\u1100-\u11FF]/,
'Ethiopic': /[\u1200-\u137F]/,
'Cherokee': /[\u13A0-\u13FF]/,
'Ogham': /[\u1680-\u169F]/,
'Runic': /[\u16A0-\u16FF]/,
'Tagalog': /[\u1700-\u171F]/,
'Hanunoo': /[\u1720-\u173F]/,
'Buhid': /[\u1740-\u175F]/,
'Tagbanwa': /[\u1760-\u177F]/,
'Khmer': /[\u1780-\u17FF]/,
'Mongolian': /[\u1800-\u18AF]/,
'Limbu': /[\u1900-\u194F]/,
'Tai_Le': /[\u1950-\u197F]/
for(var language in unicodeMap) {
if(ALLOWED_SCRIPTS.indexOf(language) > -1) {
var regex = unicodeMap[language];
if(regex.test(str)) {
return false;
return true;
function getGoogleAdsFormattedDate(d, format){
var date = new Date();
date.setDate(date.getDate() - d);
return Utilities.formatDate(date,AdsApp.currentAccount().getTimeZone(),format);
Copy link

Hi! Just the script I was looking for, but it doesn't seem to work with Youtube channels? It gives an error "This type of criterion cannot target YouTube websites". Is this possible to fix? Cheers!

Copy link

Thank you for this! works well

Copy link

I modified this to work for ONLY Youtube Channels. Thank you for the base/template -- Optmyzr rules!

Copy link

arillera commented Nov 6, 2023

I modified this to work for ONLY Youtube Channels. Thank you for the base/template -- Optmyzr rules!

How did you modify it to work for YouTube channels? Would love to do the same.

Copy link

Hello, @arillera, did you find a solution?

Copy link

Hello, @blastofff, did you find a solution?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment