Cheap VPS & Xen Server

Residential Proxy Network - Hourly & Monthly Packages

Here’s an AdWords script to apply shared campaign negative lists everywhere

Here’s an AdWords script to apply shared campaign negative lists everywhere

Do you use shared campaign negative lists? Shared lists are really useful for making sure all of your campaigns are excluding the same terms. Constantly mining search query reports means constantly finding new negatives, after all.

There are also shared lists for campaign placement exclusions, so you can make sure things like and random YouTube videos are excluded from all Display campaigns.

But how do you know if a campaign is missing a shared list?

When you’ve made a batch of new campaigns, it’s a hassle to apply shared lists to all of them, and it’s easy to accidentally leave one off. Then, it’s hard to check if you’ve done it right.

If you go to the Shared Library in your account, you can go to each list and see the names of the campaigns that share the list, but you have to do that separately for each list. You can’t see more than 500 campaign names at a time, and you can’t just download those names to check them all with your Excel-fu. And, shared lists aren’t in Editor — you’re stuck with the clunky web interface.


But you’re not actually stuck with the clunky web interface. You have AdWords Scripts!

Okay, fine, you have to use the interface a little. But you just have to set up the right shared lists once. Once you’ve got a single campaign right, this Script can handle the rest — it looks at that campaign and uses it as a template, copying its shared lists to all the other campaigns.

At Brainlabs (my employer), we use this to avoid wasting time setting up the same things everywhere, and so it’s easy to add all the necessary negatives when a new campaign gets added a few months down the line. We also have an API version that works with Bing Ads and can work across many different accounts.

Obviously, you may not want the exact same set of lists in all campaigns. And there might be a list you only use with a couple of campaigns. So you have options to define which campaigns the lists are copied to. The script doesn’t remove any of the existing lists, it only adds — so when you’ve created some extra campaigns, you can rerun it, and it won’t mess with any extra lists you’ve applied to the old campaigns.

Want this in your account? Just copy the code below into a new AdWords Script, and change some of the options at the top:

  • There are two arrays, campaignNameContains and campaignNameDoesNotContain, used to filter which campaigns are affected. For example, if campaignNameContains is [“Brand”, “Generic”] then only campaigns with names containing “brand” or “generic” are included. If campaignNameDoesNotContain is [“Display”, “Competitor”] then any campaigns with names containing “display” or “competitor” are ignored.
    • This is not case-sensitive.
    • Leave blank, [], to not exclude any campaigns.
    • If you need to put a double quote into campaignNameContains or campaignNameDoesNotContain, put a backslash before it. For example, if the campaign name is 9″ Tablets, you’d enter it as 9\” Tablets.
    • If you’ve got campaigns split by match type, you could use something like “-Exact” or “|EM” to show which campaigns are Exact match; you could add that to campaignNameDoesNotContain so the lists only get added to Broad or Phrase match campaigns.
  • If ignorePausedCampaigns is true, then the script will only look at currently active campaigns. Set them to false if you want to look at currently paused campaigns.
  • campaignToCopy is the name of the template campaign, whose shared lists shall be copied. This is case-sensitive.
    • The template campaign can be paused, but it can’t be removed.
  • Set copyNegativeKeywordLists to true to share campaign negative keyword lists.
  • Set copyExcludedPlacementLists to true to share campaign excluded placement keyword lists.
    • Obviously, at least one of copyNegativeKeywordLists and copyExcludedPlacementLists needs to be true, otherwise the script won’t have any lists to share!
  • labelName is the name of the label which will be applied to campaigns once they’ve had the lists added. This means you can see which campaigns have been covered.
    • Campaigns that already have the label will be ignored: if the Script doesn’t cover them all in one run it can go again until it covers them all.

You may want to run the script more than once for different sets of lists. For example, say you had different lists for generic and brand campaigns. You might want to do one run with campaignNameDoesNotContain set to [“Brand”] and campaignToCopy set to a non-brand campaign’s name to cover all the generics.

Then, you could change the settings so campaignNameDoesNotContain is [], campaignNameContains is [“Brand”] and campaignToCopy is a brand campaign, allowing you to do a run on just the brand campaigns.


  • AdWords Scripts can’t apply lists to Shopping, AdWords Express, Video or Universal App campaigns. Campaigns of these types are ignored.
  • The Script can’t tell the difference between “Search,” “Display” and “Search with Display Select” campaigns. You have to use the campaign names to make sure negative lists intended for Search don’t end up in Display campaigns.
  • Scripts can only run for 30 minutes, which may not be enough if you’ve got thousands of campaigns and lots of lists. But if the script times out, you can just run it again — it will say in the logs when everything has been covered.
* Shared Negative List Copying
* This script takes the shared campaign negative lists and excluded placement lists
* applied to one template campaign and applies them to all other campaigns that
* match the filters.
* Version: 1.0
* Google AdWords Script maintained on
var campaignNameDoesNotContain = [];
// Use this if you want to exclude some campaigns.
// For example [“Display”] would ignore any campaigns with ‘Display’ in the name,
// while [“Display”,”Competitors”] would ignore any campaigns with ‘display’ or
// ‘competitors’ in the name. Case insensitive.
// Leave as [] to not exclude any campaigns.
var campaignNameContains = [];
// Use this if you only want to look at some campaigns.
// For example [“Brand”] would only look at campaigns with ‘Brand’ in the name,
// while [“Brand”,”Generic”] would only look at campaigns with ‘brand’ or ‘generic’
// in the name. Case insensitive.
// Leave as [] to include all campaigns.
var ignorePausedCampaigns = false;
// Set this to true to only look at currently active campaigns.
// Set to false to also include campaigns that are currently paused.
// This is the name of the template campaign which has the desired lists already applied.
// All lists shared with this campaign will be shared with the other campaigns.
// Case sensitive!
var copyNegativeKeywordLists = true;
// Set this to true to copy shared campaign negatives.
var copyExcludedPlacementLists = true;
// Set this to true to copy shared excluded placements.
var labelName = Shared lists done;
// Once a campaign has had all the lists added, it will be labelled with this.
function main() {
if (!copyNegativeKeywordLists && !copyExcludedPlacementLists) {
throw(The options say not to copy either type of list. Please set at least one of copyNegativeKeywordLists or copyExcludedPlacementLists to true.);
// Get the lists shared with the template campaign
var templateCampaigns = AdWordsApp.campaigns()
.withCondition(CampaignName = “ + campaignToCopy.replace(//g,\\\”) + )
.withCondition(CampaignStatus IN [ENABLED, PAUSED])
if (!templateCampaigns.hasNext()) {
throw(No template campaign called ‘ + campaignToCopy + ‘ found.);
// There should be precisely one campaign in the iterator, because there should be
// precisely one template campaign to look at. So we don’t use a while loop, we just
// look at the first campaign in the iterator.
var templateCampaign =;
var typesOfList = [];
var listObjectsToAdd = [];
if (copyNegativeKeywordLists) {
if (copyExcludedPlacementLists) {
for (var i=0; i<typesOfList.length; i++) {
var iterator = templateCampaign[typesOfList[i]]().get();
while (iterator.hasNext()) {
var listObject =
var totalListsToAdd = 0;
for (var i=0; i<typesOfList.length; i++) {
Logger.log(listObjectsToAdd[i].length + + typesOfList[i] + found);
totalListsToAdd += listObjectsToAdd[i].length;
if (totalListsToAdd == 0) {
throw(No + typesOfList.join( or ) + found to copy. Please check they are applied to template campaign ‘ + campaignToCopy + ‘.);
// Get all the campaign IDs (based on campaignNameDoesNotContain, campaignNameContains
// and ignorePausedCampaigns options).
// This ignores labelling – if there are no campaigns it must be because the options
// are set incorrectly, so it throws an error.
var campaignIds = getCampaignIds();
// Find or create the campaign label
var labels = AdWordsApp.labels().withCondition(Name = ‘ + labelName + ).get();
if (!labels.hasNext()) {
// If the label does not exist, we create it
labels = AdWordsApp.labels().withCondition(Name = ‘ + labelName + ).get();
if (AdWordsApp.getExecutionInfo().isPreview() && !labels.hasNext()) {
// We can’t create labels when previewing scripts, so if this is a preview run
// and the label still doesn’t exist we use a dummy value for the ID
// (as we know nothing can be labelled with the non-existent label anyway)
var labelId = 0;
} else {
var labelId =;
var listAddMethods = {};
listAddMethods[negativeKeywordLists] = addNegativeKeywordList;
listAddMethods[excludedPlacementLists] = addExcludedPlacementList;
// Make an iterator of the campaigns that match the settings and are not labelled
var campaigns = AdWordsApp.campaigns()
.withCondition(CampaignId IN [ + campaignIds.join(,) + ])
.withCondition(Labels CONTAINS_NONE [ + labelId + ])
var campaignCount = 0;
// Go through each campaign and apply the lists
while (campaigns.hasNext()) {
var campaign =;
for (var i=0; i<typesOfList.length; i++) { // Loop over the types of list
for (var j=0; j< listObjectsToAdd[i].length; j++) { // Loop over the lists to add
campaign[listAddMethods[typesOfList[i]]](listObjectsToAdd[i][j]); // Add the list
campaign.applyLabel(labelName); // Label the campaign now the lists have been applied
if (campaignCount%100 == 0) {
Logger.log(Applied lists to + campaignCount + campaigns so far);
if (campaignCount == 0) {
Logger.log(campaignIds.length + campaigns match the settings, but all were labelled with ‘ + labelName + ‘. This suggests the lists have been applied to everything.);
} else {
Logger.log(Finished. Lists applied to + campaignCount + campaigns.);
// Get the IDs of campaigns which match the given options
function getCampaignIds() {
var whereStatement = WHERE ;
var whereStatementsArray = [];
var campaignIds = [];
if (ignorePausedCampaigns) {
whereStatement += CampaignStatus = ENABLED ;
} else {
whereStatement += CampaignStatus IN [‘ENABLED’,’PAUSED’] ;
for (var i=0; i<campaignNameDoesNotContain.length; i++) {
whereStatement += AND CampaignName DOES_NOT_CONTAIN_IGNORE_CASE ‘ + campaignNameDoesNotContain[i].replace(//g,\\\”) + ;
if (campaignNameContains.length == 0) {
whereStatementsArray = [whereStatement];
} else {
for (var i=0; i<campaignNameContains.length; i++) {
whereStatementsArray.push(whereStatement + AND CampaignName CONTAINS_IGNORE_CASE “ + campaignNameContains[i].replace(//g,\\\”) + );
for (var i=0; i<whereStatementsArray.length; i++) {
var adTextReport =
SELECT CampaignId +
whereStatementsArray[i] +
var rows = adTextReport.rows();
while (rows.hasNext()) {
var row =;
if (campaignIds.length == 0) {
throw(No campaigns found with the given settings.);
return campaignIds;