
Automate Keyword Targeting in Google Ads with Custom Script
In Google Ads, it’s common for Google to decide where ads appear, sometimes leading to unwanted impressions and wasted ad spend. This guide shows you how to take control by automating keyword targeting through a Google Ads script. Using a Google Sheet, you can specify exact, phrase, and broad match keywords for precise ad targeting. Follow these simple steps to improve your ad performance and save on costs!
Why Use Automated Keyword Targeting?
Google Ads’ automated keyword matching can lead to ads showing for irrelevant terms. By automating keyword control with a custom script, you can:
- Minimize Wasted Spend: Reduce unnecessary clicks by filtering out irrelevant terms.
- Increase ROI: Target high-intent search terms, improving click-through rates (CTR) and conversions.
- Customize Frequency: Run the script hourly, daily, or weekly, so your campaigns always have updated keyword targeting.
How This Solution Works
This automated setup uses a Google Ads script that pulls keywords from a Google Sheet. You specify:
- The keywords to target.
- The match type (exact, phrase, or broad).
- The Google Ads campaign you want to apply the keywords to.
The script then runs on a schedule to keep targeting up-to-date.
Step 1: Create the Google Sheet
First, set up a Google Sheet with the keywords, match types, and campaign names. Here’s how:
- Open Google Sheets and create a new sheet.
- Name the sheet, e.g., “Keyword Control Sheet.”
- Create the following columns in Row 1:
- Campaign Name: Name of the campaign you want to target.
- Keyword: The keyword you want to add.
- Match Type: Either “broad,” “phrase,” or “exact.”
- Copy the URL of the Google Sheet for later use in the script.
Step 2: Add and Configure the Google Ads Script
- Go to Google Ads Scripts and sign in to your account.
- Navigate to Tools & Settings > Bulk Actions > Scripts.
- Click the + button to create a new script.
- Name your script, e.g., “Automated Keyword Targeting.”
- Copy and paste the script below into the editor.
Here’s your Google Apps Script in a format ready to embed into the Google Apps Script editor:
function main() {
var sheetUrl = "Insert Your Google Sheet URL"; // Insert your Google Sheet URL
var sheetName = "Keyword Template"; // Sheet with campaign search terms
var errorLogSheetName = "ErrorLogs"; // Sheet to log errors
Logger.log("Starting script execution...");
try {
var sheet = SpreadsheetApp.openByUrl(sheetUrl).getSheetByName(sheetName);
if (!sheet) throw new Error("Main sheet '" + sheetName + "' not found.");
Logger.log("Main sheet '" + sheetName + "' opened successfully.");
var errorLogSheet = SpreadsheetApp.openByUrl(sheetUrl).getSheetByName(errorLogSheetName);
if (!errorLogSheet) {
Logger.log("Error log sheet not found, creating it...");
errorLogSheet = SpreadsheetApp.openByUrl(sheetUrl).insertSheet(errorLogSheetName);
errorLogSheet.appendRow(['Time', 'Campaign', 'Keyword', 'Status', 'Error Details']);
}
Logger.log("Error log sheet '" + errorLogSheetName + "' opened or created successfully.");
} catch (e) {
Logger.log("Error: " + e.message);
return;
}
var data = sheet.getDataRange().getValues();
// Group allowed search terms by campaign
var campaignData = {};
for (var i = 1; i < data.length; i++) {
var campaignName = data[i][0]; // Column A: Campaign Name
var searchTerm = data[i][1]; // Column B: Search Term
var matchType = data[i][2]; // Column C: Match Type (broad, phrase, exact)
if (campaignName && searchTerm && matchType) {
if (!campaignData[campaignName]) {
campaignData[campaignName] = [];
}
campaignData[campaignName].push({ term: searchTerm, matchType: matchType });
}
}
// Iterate over each campaign in the Google Sheet
for (var campaignName in campaignData) {
var allowedSearchTerms = campaignData[campaignName];
Logger.log("Processing campaign: " + campaignName);
try {
// Find the campaign in Google Ads that matches the name in the Google Sheet
var campaignIterator = AdsApp.campaigns()
.withCondition('Name = "' + campaignName + '"')
.get();
if (campaignIterator.hasNext()) {
var campaign = campaignIterator.next();
Logger.log("Found campaign: " + campaignName);
// Step 1: Retrieve Existing Negative Keywords from the Campaign
var existingNegatives = getExistingNegativeKeywords(campaign);
// Step 2: Add Negative Keywords for All Search Terms Except Allowed Ones
Logger.log("Adding negative keywords for campaign: " + campaignName);
addNegativeKeywordsForAllExcept(campaign, allowedSearchTerms, existingNegatives, errorLogSheet, campaignName);
Logger.log("Processed negative keywords for campaign: " + campaignName);
} else {
throw new Error("No campaign found with the name: " + campaignName);
}
} catch (e) {
Logger.log("Error processing campaign: " + campaignName + " - " + e.message);
logError(errorLogSheet, campaignName, "", "Error processing campaign", e.message);
}
}
Logger.log("Script execution completed.");
}
// Function to retrieve existing negative keywords from the campaign
function getExistingNegativeKeywords(campaign) {
Logger.log("Retrieving existing negative keywords for campaign: " + campaign.getName());
var existingNegatives = [];
try {
var negativeKeywordIterator = campaign.negativeKeywords().get();
while (negativeKeywordIterator.hasNext()) {
var negativeKeyword = negativeKeywordIterator.next();
existingNegatives.push(negativeKeyword.getText());
}
Logger.log("Retrieved " + existingNegatives.length + " existing negative keywords for campaign: " + campaign.getName());
} catch (e) {
Logger.log("Error retrieving negative keywords for campaign: " + campaign.getName() + " - " + e.message);
}
return existingNegatives;
}
// Function to add negative keywords for all terms except the allowed ones
function addNegativeKeywordsForAllExcept(campaign, allowedSearchTerms, existingNegatives, errorLogSheet, campaignName) {
Logger.log("Retrieving search queries for campaign: " + campaignName);
// Get the search query report or search terms that have triggered ads
var report = AdsApp.report(
"SELECT Query FROM SEARCH_QUERY_PERFORMANCE_REPORT " +
"WHERE CampaignId = " + campaign.getId() +
" AND Impressions > 0");
var queryRows = report.rows();
var count = 0; // Counter to throttle API requests
while (queryRows.hasNext()) {
var row = queryRows.next();
var searchTerm = row['Query'];
Logger.log("Processing search term: " + searchTerm);
try {
// Check if the search term is NOT in the allowed list and hasn't already been negated
if (!isAllowedSearchTerm(searchTerm, allowedSearchTerms) && existingNegatives.indexOf(searchTerm) === -1) {
// Check if the keyword exceeds the limits (10 words or 80 characters)
if (isKeywordInvalid(searchTerm)) {
Logger.log("Keyword exceeds limit: " + searchTerm);
logError(errorLogSheet, campaignName, searchTerm, "Keyword exceeds limits", "");
} else {
// Add it as a negative keyword in exact match format by default
var exactMatchTerm = '[' + searchTerm + ']';
// Only attempt to add if it's not already in the existing negatives
if (existingNegatives.indexOf(exactMatchTerm) === -1) {
Logger.log("Attempting to add negative keyword: " + exactMatchTerm);
campaign.createNegativeKeyword(exactMatchTerm);
Logger.log("Successfully added negative keyword: " + exactMatchTerm);
// Throttle API requests: Pause for 1 second after every 50 keywords
count++;
if (count >= 50) {
Logger.log("Throttling API requests, pausing for 1 second.");
Utilities.sleep(1000); // Pause for 1 second
count = 0; // Reset the counter
}
} else {
Logger.log("Skipping duplicate negative keyword: " + exactMatchTerm);
}
}
}
} catch (e) {
Logger.log("Error adding negative keyword: " + searchTerm + " - " + e.message);
logError(errorLogSheet, campaignName, searchTerm, "Error adding negative keyword", e.message);
}
}
}
// Function to check if a search term is in the allowed list
function isAllowedSearchTerm(searchTerm, allowedSearchTerms) {
for (var i = 0; i < allowedSearchTerms.length; i++) {
var allowedTerm = allowedSearchTerms[i].term;
var matchType = allowedSearchTerms[i].matchType.toLowerCase();
if (matchType === "broad" && searchTerm.includes(allowedTerm)) {
return true;
} else if (matchType === "phrase" && searchTerm.indexOf(allowedTerm) > -1) {
return true;
} else if (matchType === "exact" && searchTerm === allowedTerm) {
return true;
}
}
return false;
}
// Function to check if the keyword exceeds the 10-word or 80-character limit
function isKeywordInvalid(searchTerm) {
var wordCount = searchTerm.split(" ").length;
var characterCount = searchTerm.length;
return (wordCount > 10 || characterCount > 80);
}
// Function to log errors in a Google Sheet
function logError(sheet, campaign, keyword, status, errorMessage) {
var timestamp = new Date();
sheet.appendRow([timestamp, campaign, keyword, status, errorMessage]);
}
Script Breakdown
- SPREADSHEET_URL: Replace
YOUR_GOOGLE_SHEET_URL
with the link to your Google Sheet.
Spread Sheet Template : Access from Here, Make a copy of the sheet.
- Data Fetching: The script pulls keyword data from each row in the sheet.
- Campaign Selection: It matches each campaign name in the sheet to your Google Ads account.
- Match Type Handling: Depending on whether the match type is broad, phrase, or exact, the script adjusts the keyword format for Google Ads.
Step 3: Set Script Scheduling
Now, set up the frequency for running the script. Here’s how:
- In the Google Ads script editor, click on Preview to test if the script is working.
- Once confirmed, click on Run to apply the changes immediately.
- To schedule the script, click on Actions > Create Schedule and select a frequency (e.g., hourly, daily, weekly).
Step 4: Monitor Search Terms and Refine Keywords
Once your script runs, regularly review the search terms report in Google Ads:
- Go to Campaigns > Keywords > Search Terms.
- Review the terms triggering your ads.
- Add irrelevant terms as negative keywords to prevent your ads from showing for these searches.
Benefits of Using Automated Keyword Targeting Scripts
- More Precise Targeting: Ensure your ads only show for keywords that matter.
- Reduced Wasted Spend: Avoid clicks on irrelevant searches, making your budget more efficient.
- Scalable Management: As your keyword list grows, simply add new terms to the Google Sheet.
Automating keyword targeting in Google Ads gives you control over your ad placements, minimizes wasted spend, and improves campaign performance. By using this script along with a simple Google Sheet, you can take charge of where your ads show up and continuously optimize your campaigns. Try this approach and watch your ad performance improve!
Script Credits : Artur MacLellan