I'm trying to create a script that excludes YouTube-channels based on a list of characters I don't want to be targeting.
I don't know much about coding nor creating scripts, so I'm using ChatGPT to do it, as I haven't found a script that does it like I want it to.
This is the script I created so far:
var config = {
LOG: true,
EXCLUSIONS_LIST: "DK - Masimo negative YouTube channels", // 🎯 Exclusion list at the account level
IMPRESSION_THRESHOLD: 0, // Minimum number of impressions before exclusion
DAYS_TO_CHECK: 30, // Scans YouTube placements from the last 30 days
EXCLUDE_TERMS: [ // 🎯 Unwanted characters or words in the placement name
"Ç", "گ", "و", "د", "ا", "ل", "ك", "ت", "ب", "ل", "ه",
"в", "Д", "д", "ь", "ق", "ر", "آ", "ن", "ن", "و", "ر", "ه", "ي", "ة",
"ж", "б", "ч", ".xyz", ".pl", "spil", "spill", "spel", "spiel", "plays",
".store", ".site", ".space", ".gg", "game", "kids", "congo", "poki",
"柴", "犬", "春", "卷", "的", "英", "國", "日", "常"
]
};
function main() {
var excludedChannelIds = [];
// Calculate date range
var today = new Date();
var startDate = new Date(today.getTime() - (config.DAYS_TO_CHECK * 24 * 60 * 60 * 1000));
var formattedToday = Utilities.formatDate(today, AdsApp.currentAccount().getTimeZone(), "yyyyMMdd");
var formattedStartDate = Utilities.formatDate(startDate, AdsApp.currentAccount().getTimeZone(), "yyyyMMdd");
Logger.log("🔍 Scanning YouTube placements from: " + formattedStartDate + " to " + formattedToday);
// ✅ SQL query: Matches **PLACEMENT NAME** from Google Ads interface
var query = "SELECT group_placement_view.target_url, group_placement_view.display_name " +
"FROM group_placement_view " +
"WHERE group_placement_view.placement_type = 'YOUTUBE_CHANNEL' " +
"AND metrics.impressions > " + config.IMPRESSION_THRESHOLD + " " +
"AND segments.date BETWEEN '" + formattedStartDate + "' AND '" + formattedToday + "'";
var report = AdsApp.report(query);
var rows = report.rows();
if (!rows.hasNext()) {
Logger.log("⚠️ No YouTube placements found in the period.");
return;
}
while (rows.hasNext()) {
var row = rows.next();
var placementUrl = row['group_placement_view.target_url']; // YouTube channel URL
var placementDisplayName = row['group_placement_view.display_name']; // 🎯 Placement name from UI
if (!placementDisplayName) {
Logger.log("⚠️ Ignoring placement without a name.");
continue; // Ignore placements without a name
}
placementDisplayName = placementDisplayName.toString().trim(); // Ensure correct format
Logger.log("🔍 Reviewing placement: " + placementDisplayName + " (" + placementUrl + ")");
if (shouldExcludePlacement(placementDisplayName)) {
var channelId = extractYouTubeChannelId(placementUrl);
if (channelId) {
excludedChannelIds.push(channelId);
Logger.log("❌ **Marked for exclusion**: " + placementDisplayName + " -> " + channelId);
} else {
Logger.log("⚠️ Could not extract YouTube channel ID from: " + placementUrl);
}
}
}
// ➕ Add excluded placements to the exclusion list
if (excludedChannelIds.length > 0) {
var exclusionList = getOrCreatePlacementExclusionList(config.EXCLUSIONS_LIST);
addPlacementsToExclusionList(exclusionList, excludedChannelIds);
} else {
Logger.log("✅ **No placements matched the exclusion criteria.**");
}
}
// 🔍 Check if the placement name contains **one or more** unwanted characters
function shouldExcludePlacement(name) {
for (var i = 0; i < config.EXCLUDE_TERMS.length; i++) {
if (name.includes(config.EXCLUDE_TERMS[i])) {
Logger.log("⚠️ Found unwanted character '" + config.EXCLUDE_TERMS[i] + "' in placement name: " + name);
return true;
}
}
return false;
}
// 📌 Extract YouTube channel ID from URL
function extractYouTubeChannelId(url) {
var match = url.match(/youtube\.com\/channel\/([a-zA-Z0-9_-]+)/);
return match ? match[1] : null;
}
// 🔧 Retrieve or create an exclusion list at the account level
function getOrCreatePlacementExclusionList(listName) {
var lists = AdsApp.excludedPlacementLists().withCondition("Name = '" + listName + "'").get();
if (lists.hasNext()) {
Logger.log("✅ Exclusion list found: " + listName);
return lists.next();
} else {
Logger.log("🆕 Creating new exclusion list: " + listName);
return AdsApp.newExcludedPlacementListBuilder().withName(listName).build();
}
}
// ➕ **Correctly add placements to the exclusion list**
function addPlacementsToExclusionList(exclusionList, channelIds) {
if (channelIds.length === 0) {
Logger.log("❌ No valid YouTube channel IDs found.");
return;
}
try {
exclusionList.addExcludedYouTubeChannels(channelIds);
Logger.log("✅ Added " + channelIds.length + " YouTube channels to the exclusion list.");
} catch (e) {
Logger.log("❌ Error while adding: " + e.message);
}
}
The script I made up until now is doing what I ask it to do (finding YT-channels where the channel name include one of my unwanted chraracters e.g.: 能). After finding the channel it is then supposed to add the channel to an exclusion list by sending the Channel ID or the URL to the exclusion list, but here it does nothing, as you can see below:

There are also none actions in the changes column as you can see:

Therefore ChatGPT asked me to run a test to see if I could add a YouTube placement to the exclusion list via script, at all.
Thus it made an example of a script where we should test if the ability to add negative YT-accounts to an exclusion list was possible at all. We're doing so by giving the script one specific YT-channel to exclude (UCkD0NX-oq1BOQ7cxl7VDQww)
This is the test script:
function main() { testExclusion();}function testExclusion() { var listName = "DK - Masimo negative YouTube channels"; var testChannelId = "UCkD0NX-oq1BOQ7cxl7VDQww"; // Dummy test channel ID var testChannelUrl = "https://www.youtube.com/channel/" + testChannelId; // Correct format for YouTube channels Logger.log("🔍 Testing manual addition of channel ID: " + testChannelId); var lists = AdsApp.excludedPlacementLists() .withCondition("Name = '" + listName + "'") .get(); if (!lists.hasNext()) { Logger.log("❌ The exclusion list '" + listName + "' was not found!"); return; } var exclusionList = lists.next(); Logger.log("✅ Exclusion list found: " + listName); try { // Correctly adds the placement to the exclusion list exclusionList.addExcludedPlacements([testChannelUrl]); Logger.log("✅ Test channel successfully attempted to be added to the exclusion list: " + testChannelUrl); } catch (e) { Logger.log("❌ Error while adding: " + e.message); }}Here the script is actually trying to add the placement but I get this error code under the column 'Changes':

I feel like I'm really close but I just don't know how til fix this issue with actually adding the specific YT-URL or YT-channel to the Account exclusion list.. Is it possible at all, or do I need to get the script to make the changes directly in the campaigns?Can you help me to spot where it goes wrong?
Best regards,
Jakob.