{"id":1976,"date":"2016-08-25T15:52:41","date_gmt":"2016-08-25T13:52:41","guid":{"rendered":"https:\/\/webmasterei-prange.de\/automatisches-ausschliessen-von-keywords-in-anzeigengruppen-google-ads-script\/"},"modified":"2026-03-24T16:13:09","modified_gmt":"2026-03-24T15:13:09","slug":"automatisches-ausschliessen-von-keywords-in-anzeigengruppen-google-ads-script","status":"publish","type":"post","link":"https:\/\/webmasterei-prange.de\/en\/automatisches-ausschliessen-von-keywords-in-anzeigengruppen-google-ads-script\/","title":{"rendered":"Automatisches ausschlie\u00dfen von Keywords in Anzeigengruppen [Google Ads Script]"},"content":{"rendered":"\n<p>AdWords accounts with a BROAD MATCH\/EXACT MATCH bidding strategy can be very labor-intensive. In addition to the normal work of an AdWords campaign, new EXACT Match keywords must be excluded from the BROAD Match campaign. This painstaking work is rewarded with better CPCs because it doesn&#8217;t create competition of its own. To minimize this work, we&#8217;ve developed a script that takes care of this task.   <\/p>\n\n<p><strong>The script checks campaigns with [Campaign Label], collects all keywords from the ad group(s) with the [Source Label], and adds them as negative keywords to the ad group with [Target Label]. In doing so, all existing negative keywords are deleted at the ad group level.   <\/strong><\/p>\n\n<h2 class=\"wp-block-heading\" id=\"toc_Setup_des_Auto_Exclude_AdWords_Scripts\">Setup of the Auto Exclude AdWords Script<\/h2>\n\n<ol class=\"wp-block-list\">\n<li>Place script at the MCC level.<\/li>\n\n\n\n<li>Open Google Docs Spreadsheet.<\/li>\n\n\n\n<li>Copy spreadsheet id into the script.<\/li>\n\n\n\n<li>Authorize the script.<\/li>\n\n\n\n<li>Set prefillSpreadsheet to \u201ctrue\u201d, save and run preview.<\/li>\n\n\n\n<li>Assign label names for Campaign Label, Source Label (Exact Match ad group(s)) and Target Label (Broad Match ad group(s)) in the spreadsheet.<\/li>\n\n\n\n<li>Assign corresponding labels to campaigns and ad groups.<\/li>\n\n\n\n<li>\u201cRun preview\u201d<\/li>\n\n\n\n<li>If the test is satisfactory, run the script and set it to daily execution.<\/li>\n<\/ol>\n\n<h2 class=\"wp-block-heading\" id=\"toc_Anmerkungen_Einschrankungen_Hinweise\">Notes \/ Limitations \/ Remarks<\/h2>\n\n<ul class=\"wp-block-list\">\n<li>The current version of the script has only been tested at the MCC level.<\/li>\n\n\n\n<li>The script is strongly inspired by this <a href=\"http:\/\/remkovanderzwaag.nl\/blog\/updated-adwords-script-auto-add-negative-keywords-dsa\" target=\"_blank\" rel=\"noreferrer noopener\">DSA Script<\/a>. The original script also runs at the account level. <\/li>\n\n\n\n<li>The current version has not been tested for error tolerance. Please comment on any errors in this post. <\/li>\n\n\n\n<li>Further development for additional requirements is possible. Please comment! <\/li>\n<\/ul>\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\n\/\/------------------------------------------------\n\/\/ This Script aims to Auto-Exclude Keywords  \n\/\/ from source labeled AdGroups and place them as\n\/\/ negative keywords within another AdGroup within  \n\/\/ the same campaign\n\/\/ This Script was built for Campaigns split in\n\/\/ BROAD MATCH and EXACT MATCH Keywords AdGroups.\n\/\/ More info: https:\/\/webmasterei-prange.de\/adwords-script-automatisches-auslassen-von-keywords-in-anzeigengruppen\n\/\/ Strongly inspired by: Remko van der Zwaag &amp; PDDS see: http:\/\/remkovanderzwaag.nl\/blog\/updated-adwords-script-auto-add-negative-keywords-dsa\n\/\/------------------------------------------------\n\nvar spreadsheetId = 'ENTER SPREADSHEET ID';\nvar prefillSpreadsheet = false; \/\/ When set to true, gets all accounts from the MCC account\n  \/\/ and automagically adds their name and id to the spreadsheet\n  \/\/ Use once, doesn't check for existing records\n  \/\/ switch back to false after use\n  \/\/ PREFERABLY RUN USING PREVIEW (true), CHANGE TO false AND SAVE\n\n\/\/ The label a camaign is watched\n\/\/ * Default - overwritable from spreadsheet\nvar campaign_label = 'BROAD | EXACT';\n\n\/\/ The label on the source campaign(s)\n\/\/ * Default - overwritable from spreadsheet\nvar adgroup_source_label = 'EXACT';\n\n\/\/ The label on the target group\n\/\/ * Default - overwritable from spreadsheet\nvar adgroup_target_label = 'BROAD';\n\n\nfunction mapRowToInfo(row) {\n  return {\n  custId: row[1].trim(),\n  cust: row[0],\n  campaign_label: row[2],\n  adgroup_source_label: row[3],\n  adgroup_target_label: row[4]\n  };\n}\n\n\nfunction main() {\n  try {\n  if (prefillSpreadsheet) {\n  MccApp.accounts()\n  .withLimit(50)\n  .executeInParallel(\"getSSAccountInfo\",\"saveSSAccountInfo\");\n  } else {\n  var ids = getSpreadsheetIds();\n  if (ids.length &gt; 0) {\n  MccApp.accounts()\n  .withIds(ids)\n  .withLimit(50)\n  .executeInParallel(\"processAccount\");\n  }\n  }\n  } catch (e) {\n  processAccount();\n  }\n}\n\nfunction getSSAccountInfo() {\n  var result = {\n  custId: AdWordsApp.currentAccount().getCustomerId(),\n  cust: AdWordsApp.currentAccount().getName()\n  };\n  Logger.log(result);\n  return JSON.stringify(result);\n}\n\nfunction saveSSAccountInfo(response) {\n  var ss;\n  try {\n  ss = SpreadsheetApp.openById(spreadsheetId);\n  } catch (e) {\n  }\n  ss = ss.getSheets()[0];\n  ss.appendRow([\"Account Name\", \"Account ID\", \"Campaign Label\", \"Source Label\", \"Target Label\"]);\n  for (var i in response) {\n  if(!response[i].getReturnValue()) { continue; }\n  var rep = JSON.parse(response[i].getReturnValue());\n  Logger.log(rep);\n  ss.appendRow([rep.cust, rep.custId]);\n  }\n}\n\nfunction getSpreadsheetIds() {\n  var ids = [],\n  ss,\n  reAWId = \/^([0-9]{3})-([0-9]{3})-([0-9]{4})$\/;\n\n  try {\n  ss = SpreadsheetApp.openById(spreadsheetId);\n  } catch (e) {\n  return ids;\n  }\n  ss = ss.getSheets()[0];\n  var rows = parseInt(ss.getLastRow());\n  var range = ss.getRange(\"A1:Z\" + rows).getValues();\n  for (var i = 0; i  &lt;  rows; i++) {\n  var account = mapRowToInfo(range[i]);\n  if (!reAWId.test(account.custId) || account.skip) {\n  continue;\n  }\n  ids.push(account.custId);\n  }\n  return ids;\n}\n\nfunction getAccountInfo() {\n  var ss;\n  var reAWId = \/^([0-9]{3})-([0-9]{3})-([0-9]{4})$\/;\n  var protoAccount = {\n  custId: AdWordsApp.currentAccount().getCustomerId(),\n  cust: AdWordsApp.currentAccount().getName(),\n  include_label: campaign_label,\n  source_label: adgroup_source_label,\n  target_label: adgroup_target_label\n  };\n  try {\n  ss = SpreadsheetApp.openById(spreadsheetId);\n  } catch (e) {\n  return protoAccount;\n  }\n  ss = ss.getSheets()[0];\n  var rows = parseInt(ss.getLastRow());\n  var range = ss.getRange(\"A1:Z\" + rows).getValues();\n  var ret_account;\n\n  for (var i = 0; i  &lt;  rows; i++) {\n  var account = mapRowToInfo(range[i]);\n  if (account.skip) {\n  continue;\n  }\n  if (!reAWId.test(account.custId) || account.custId !== protoAccount.custId) {\n  continue;\n  }\n\n  for(var key in account) {\n  if (account[key] === '' || account[key] === '_ALL_') {\n  account[key] = protoAccount[key];\n  }\n  }\n  return account;\n  }\n  return protoAccount;\n}\n\n\nfunction processAccount() {\n  var account = getAccountInfo();\n  try {\n  var campaigns = getCampaignsWithLabel(account.campaign_label);\n  } catch (ex) {\n  return;\n  }\n  if (campaigns.length === 0) {\n  return;\n  }\n  var include_list = [];\n  if (account.campaign_label !== '') {\n  try {\n  var iterator = getCampaignsWithLabel(account.campaign_label);\n  while (iterator.hasNext()) {\n  include_list.push(iterator.next().getId());\n  }\n  } catch (ex) {\n  \/\/ pass\n  }\n  }\n  while (campaigns.hasNext()) {\n  var campaign = campaigns.next();\n  var campaignName = campaign.getName()\n  var keywords = getSourceAdgroup(adgroup_source_label,campaignName);\n  setTargetAdgroupKeywords(adgroup_target_label,keywords,campaignName);\n  }\n}\n \nfunction getCampaignWithLabel(label) {\n  var campaignIterator = getCampaignsWithLabel(label);\n\n  if (campaignIterator.hasNext()) {\n  return campaignIterator.next();\n  }\n\n  return null;\n}\n\nfunction getCampaignsWithLabel(label) {\n  \n  return AdWordsApp.campaigns()\n  .withCondition(\"LabelNames CONTAINS_ANY ['\" + label + \"']\")\n  .get();\n}\n\nfunction getCampaignsWithLabelArray(label) {\n  var iterator = getCampaignsWithLabel(label);\n  var campaigns = [];\n  while (iterator.hasNext()) {\n  campaigns.push(iterator.next());\n  }\n  return campaigns;\n}\n\nfunction getSourceAdgroup(adgroup_source_label,campaignName){\n  var adGroupSelector = AdWordsApp\n  .adGroups()\n  .withCondition(\"CampaignName = '\" + campaignName + \"'\")\n  .withCondition(\"LabelNames CONTAINS_ANY ['\" + adgroup_source_label + \"']\")\n  .withCondition(\"Status = ENABLED\");\n  var adGroupIterator = adGroupSelector.get();\n  var keywords = [];\n  while (adGroupIterator.hasNext()) {\n  var adGroup = adGroupIterator.next();\n  keywords = keywords.concat(getKeyWords(adGroup));\n  }\n  return keywords;\n}\n\nfunction getKeyWords(adGroup){\n  var keywords = []\n  var keywordSelector = AdWordsApp\n  .keywords()\n  .withCondition(\"AdGroupName = '\" + adGroup.getName() + \"'\")\n  .withCondition(\"Status = ENABLED\");\n\n  var keywordIterator = keywordSelector.get();\n  while(keywordIterator.hasNext()){\n  var keyword = keywordIterator.next();\n  keyword = keyword.getText();\n  keywords.push(keyword)\n  }\n  return keywords;\n}\n\nfunction setTargetAdgroupKeywords(label,keywords,campaignName){\n  var adGroupSelector = AdWordsApp\n  .adGroups()\n  .withCondition(\"LabelNames CONTAINS_ANY ['\" + label + \"']\")\n  .withCondition(\"CampaignName = '\" + campaignName + \"'\")\n  var adGroupIterator = adGroupSelector.get();\n  \n  while (adGroupIterator.hasNext()) {\n  var adGroup = adGroupIterator.next();\n  removeNegativeKeywords(adGroup);\n  setNegativeKeywords(keywords,adGroup);\n  }\n}\n\nfunction removeNegativeKeywords(targetAdGroup){\n  var negativeKeywordSelector = targetAdGroup.negativeKeywords();\n  var negativeKeywordIterator = negativeKeywordSelector.get();\n  while (negativeKeywordIterator.hasNext()) {\n  var negativeKeyword = negativeKeywordIterator.next();\n  negativeKeyword.remove();\n  }\n}\n\nfunction setNegativeKeywords(keywords,targetAdGroup) {\n  \/\/Logger.log(targetAdGroup.getCampaign().getName()+' '+targetAdGroup.getName()+' has AdGroups: '+keywords.length+' Number of Keywords:'+keywords.length);\n  for (var i=0;i&lt;  keywords.length; i++) {\n  var keyword = keywords[i];\n  Logger.log(keyword);\n  targetAdGroup.createNegativeKeyword(keyword);\n  }\n}<\/pre>\n\n<p><a href=\"https:\/\/gist.github.com\/Webmasterei\/69765beb6fa0d1726682d0bec946318a#file-autoexcludeadgroupkeywords-js\">autoExcludeAdGroupKeyWords.js<\/a> hosted with \u2764 by <a href=\"https:\/\/github.com\/\">GitHub<\/a><\/p>\n\n<p>Changelog:<\/p>\n\n<p>V 0.01 added (tests only on MCC level)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>AdWords accounts with a BROAD MATCH\/EXACT MATCH bidding strategy can be very labor-intensive. In addition to the normal work of an AdWords campaign, new EXACT Match keywords must be excluded from the BROAD Match campaign. This painstaking work is rewarded with better CPCs because it doesn&#8217;t create competition of its&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_kad_blocks_custom_css":"","_kad_blocks_head_custom_js":"","_kad_blocks_body_custom_js":"","_kad_blocks_footer_custom_js":"","_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","slim_seo":{"title":"Google Ads Script: Automatically exclude keywords","description":"A practical guide on how to automatically exclude keywords with Labels & Script \u2013 for clean broad\/exact structure and more efficient costs."},"footnotes":""},"categories":[24,32],"tags":[],"class_list":["post-1976","post","type-post","status-publish","format-standard","hentry","category-google-ads-optimization","category-google-ads-scripts"],"taxonomy_info":{"category":[{"value":24,"label":"Google Ads Optimization"},{"value":32,"label":"Google Ads Scripts"}]},"featured_image_src_large":false,"author_info":{"display_name":"admin","author_link":"https:\/\/webmasterei-prange.de\/en\/author\/admin\/"},"comment_info":0,"category_info":[{"term_id":24,"name":"Google Ads Optimization","slug":"google-ads-optimization","term_group":0,"term_taxonomy_id":24,"taxonomy":"category","description":"","parent":0,"count":12,"filter":"raw","cat_ID":24,"category_count":12,"category_description":"","cat_name":"Google Ads Optimization","category_nicename":"google-ads-optimization","category_parent":0},{"term_id":32,"name":"Google Ads Scripts","slug":"google-ads-scripts","term_group":0,"term_taxonomy_id":32,"taxonomy":"category","description":"","parent":0,"count":10,"filter":"raw","cat_ID":32,"category_count":10,"category_description":"","cat_name":"Google Ads Scripts","category_nicename":"google-ads-scripts","category_parent":0}],"tag_info":false,"_links":{"self":[{"href":"https:\/\/webmasterei-prange.de\/en\/wp-json\/wp\/v2\/posts\/1976","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/webmasterei-prange.de\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/webmasterei-prange.de\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/webmasterei-prange.de\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/webmasterei-prange.de\/en\/wp-json\/wp\/v2\/comments?post=1976"}],"version-history":[{"count":3,"href":"https:\/\/webmasterei-prange.de\/en\/wp-json\/wp\/v2\/posts\/1976\/revisions"}],"predecessor-version":[{"id":2349,"href":"https:\/\/webmasterei-prange.de\/en\/wp-json\/wp\/v2\/posts\/1976\/revisions\/2349"}],"wp:attachment":[{"href":"https:\/\/webmasterei-prange.de\/en\/wp-json\/wp\/v2\/media?parent=1976"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/webmasterei-prange.de\/en\/wp-json\/wp\/v2\/categories?post=1976"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/webmasterei-prange.de\/en\/wp-json\/wp\/v2\/tags?post=1976"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}