A very popular way to structure Google Ads accounts is separating keywords by match types into different campaigns and/or ad groups. You can have the same keyword with different match types is a different campaign/ad group.
If you are using this type of account structure, it is prone to match type mistakes; exact match might end up in your broad match campaings or vice versa.
This script makes sure your keyword match types are consistent with the match type you indicate in the naming of your campaigns/ad groups. As a bonus it also checks to make sure your keywords are formatted correctly for the specific match types (no dangling ' + ' signs).
Happy scripting!
SETUP INSTRUCTIONS:
Don’t worry if you have never done this before. You do not need any coding skills. It is as simple as copy-paste. Simply follow these instructions on how to set up and schedule google ads scripts.
In the script you should adjust the following things:
SPREADSHEET_URL: insert a new blank spreadsheet url
EMAIL_ADDRESS: insert your email
CAMPAIGN_LABEL: insert label to select campaigns by using a label
EXACT_MATCH_IDENTIFIER: text in your campaign names and/or ad group names to indicate exact match type
BROAD_MATCH_IDENTIFIER: text in your campaign names and/or ad group names to indicate broad match type
BROAD_MODIFIED_MATCH_IDENTIFIER : text in your campaign names and/or ad group names to indicate modified broad match type
Scheduling: Let's go daily!
The script
// Copyright 2019, Nils Rooijmans, All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
var SPREADSHEET_URL = ""; //insert a new blank spreadsheet url
var EMAIL_ADDRESS = ""; //insert your email
var EMAIL_SUBJECT = "[ALERT] - Keyword Match Type Issues";
var CAMPAIGN_LABEL = ""; // Leave blank for all campaigns
// text in your campaign names and/or ad group names to indicate match type
var EXACT_MATCH_IDENTIFIER = '(exact)';
var BROAD_MATCH_IDENTIFIER = '(broad)';
var BROAD_MODIFIED_MATCH_IDENTIFIER = '(BMM)';
var LOG_ISSUE="yes" ; // Blank to stop use of Logger.log if lots of issues
var totalNrOfIssues = 0;
var issues =[];
function main() {
//prepare the Spreadsheet
var ss = SpreadsheetApp.openByUrl(SPREADSHEET_URL);
var sheet = ss.getActiveSheet();
sheet.clear(); //remove earlies alerts
var header = [
"Account Name",
"Campaign Name",
"Ad Group Name",
"Keyword",
"KeywordId",
"Issue "
];
sheet.appendRow(header);
var campaignSelector = AdWordsApp.campaigns()
.withCondition("AdvertisingChannelType = SEARCH") // Search campaings only (no display/shopping, ...)
.withCondition("CampaignTrialType = BASE") //skip Drafts and Experiments
.withCondition("Status = ENABLED") ;
if (CAMPAIGN_LABEL!="") {
campaignSelector=campaignSelector
.withCondition("LabelNames CONTAINS_ANY ['"+CAMPAIGN_LABEL+"']");
}
var campaignIterator = campaignSelector.get();
var campaignIds=[] ;
while (campaignIterator.hasNext()) {
var campaign = campaignIterator.next();
campaignIds.push(campaign.getId()) ;
}
checkKeywordMatchTypes(campaignIds);
if (issues.length > 0) { // there is at least one issue
var range = sheet.getRange(2, 1, issues.length, header.length);
range.setValues(issues);
var emailBody =
"Number of issues: " + issues.length + "\n" +
"See details: "+ SPREADSHEET_URL + "\n";
MailApp.sendEmail(EMAIL_ADDRESS, EMAIL_SUBJECT, emailBody);
}
}
function checkKeywordMatchTypes(campaignIds) {
var accountName = AdWordsApp.currentAccount().getName();
// Check Exact Match Ad Groups and Keywords
var awql="SELECT CampaignName, AdGroupName, Criteria, Id, KeywordMatchType FROM KEYWORDS_PERFORMANCE_REPORT WHERE CampaignId IN ["+campaignIds.join(",")+"] AND AdGroupStatus='ENABLED' AND Status='ENABLED'" ;
var rows=AdWordsApp.report(awql).rows() ;
while (rows.hasNext()) {
var row = rows.next();
var campaignName = row['CampaignName'].toString().trim();
var adGroupName = row['AdGroupName'].toString().trim();
var keyword = row['Criteria'].toString().trim();
var keywordId = row['Id'].toString().trim();
var keywordMatchType=row['KeywordMatchType'].toString().trim() ;
// Wrong matchtype in Exact match group
if ( ( adGroupName.toString().toLowerCase().indexOf(EXACT_MATCH_IDENTIFIER.toLowerCase())>=0 ||
campaignName.toString().toLowerCase().indexOf(EXACT_MATCH_IDENTIFIER.toLowerCase())>=0
) && (keywordMatchType.toUpperCase() != 'EXACT')
) {
logIssue("Match type issue: "+adGroupName+" | "+keyword+" : KW has match type: "+row['KeywordMatchType']);
issues.push([accountName, campaignName, adGroupName, keyword, keywordId, "Keyword Match Type issue"]);
}
else if ( adGroupName.toLowerCase().indexOf(BROAD_MODIFIED_MATCH_IDENTIFIER.toLowerCase())>=0 ||
campaignName.toLowerCase().indexOf(BROAD_MODIFIED_MATCH_IDENTIFIER.toLowerCase())>=0
) {
if (keywordMatchType.toUpperCase() != 'BROAD') {
logIssue("Match type issue: "+adGroupName+" | "+keyword+" : KW has match type: "+row['KeywordMatchType']);
issues.push([accountName, campaignName, adGroupName, keyword, keywordId, "Keyword Match Type issue"]);
} else if (!isValidMBMKeyword(keyword)) {
logIssue("Match type issue: "+adGroupName+" | "+keyword+" : KW is not a valid MBM keyword");
issues.push([accountName, campaignName, adGroupName, keyword, keywordId, "MBM Keyword validation issue"]);
}
}
else if ( adGroupName.toLowerCase().indexOf(BROAD_MATCH_IDENTIFIER.toLowerCase())>=0 ||
campaignName.toLowerCase().indexOf(BROAD_MATCH_IDENTIFIER.toLowerCase())>=0
) {
if (keywordMatchType.toUpperCase() != 'BROAD') {
logIssue("Match type issue: "+adGroupName+" | "+keyword+" : KW has match type: "+row['KeywordMatchType']);
issues.push([accountName, campaignName, adGroupName, keyword, keywordId, "Keyword Match Type issue"]);
}
else if (!isValidBroadKeyword(keyword)) {
logIssue("Match type issue: "+adGroupName+" | "+keyword+" : KW is not a valid Broad keyword");
issues.push([accountName, campaignName, adGroupName, keyword, keywordId, "Broad Keyword validation issue"]);
}
}
}
function isValidMBMKeyword(keyword) {
if(!(keyword[0]=='+')) return false; // MBM keyword needs to start with a '+'
if(/\+ /.test(keyword)) return false; // MBM keyords can not have '+' followed by a space -> the '+' needs to be immediately in front of the terms in keyword
if(/\w\+/.test(keyword)) return false; // MBM keyords can not have '+' that is immediately preceded by a " word character" -> the '+' needs to be preceded by a space
if(/\s\w/.test(keyword)) return false; // All the terms in the keyword need to be preceded by a '+' , not a whitespace
return true;
}
function isValidBroadKeyword(keyword) {
if( (keyword.toString().indexOf("+") != -1) ||
(keyword.toString().indexOf("[") != -1) ||
(keyword.toString().indexOf("]") != -1) ||
(keyword.toString().indexOf('\"') != -1)
) {
Logger.log(" * Keyword is Unvalid Broad type");
return false;
}
return true;
}
function logIssue(msg) {
if (LOG_ISSUE!="") {
Logger.log(msg) ;
}
}
}
Show whole script!
Loading Comments
The Experts
Tibbe van AstenTeam Lead Performance Marketing
Nils RooijmansWater Cooler Topics
Martijn KraanFreelance PPC Specialist
Bas BaudoinTeamlead SEA @ Happy Leads
Jermaya LeijenDigital Marketing Strategist
Krzysztof BycinaPPC Specialist from Poland
How about you?JOIN US!
Caring
Adsscripts.com is all about sharing knowledge. In the current market, PPC specialists like to keep their knowledge and experience to themselves. We're convinced that sharing knowledge can ensure that everyone gets better at their work. We want to change this by sharing our knowledge about scripts with everyone.
Do you also want to contribute? We are open to new ideas and feedback on everything you find on Adsscripts.com.