It’s the end of the month, so chances are you’re spending a lot of time thinking about PPC budgets. Will you spend the full amount you allocated for the month and do so without going over budget?
It’s pretty amazing that simple questions like this have to take so much thought from account managers. There’s gotta be an automation for this, right? Once again, AdWords Scripts to the rescue!
So read on, and I’ll point you to some useful ones that have been around awhile and share a brand-new script for allocating budgets equitably among multiple locations in hard-to-target cities.
Translating PPC budgets to business budgets
Part of the reason budgets are such a pain is that the engines think about them differently from the average company. A company sets monthly, quarterly or annual budgets. AdWords, on the other hand, relies on daily budgets.
Companies tend to think about budgets pretty broadly; for example, they might have a marketing budget with a subset of that allocated for AdWords. But AdWords works primarily with campaign budgets. Facebook gets even more granular with Ad Set budgets.
Because of the way advertisers have to set up budgets in the engines, even a simple business requirement like spending $10,000 on AdWords this month can get pretty tricky.
Solution #1: Spend your whole budget for the month
Let’s tackle the first common budget problem: how to make sure that every campaign or shared budget spends the full amount, even when there are a few days in which traffic may have been unexpectedly weak.
Google automatically handles this to some degree with overdelivery. But overdelivery is limited to 20 percent of the budget per day, so it won’t always be able to make up the difference.
It’s also a challenge for advertisers because they will often set a daily budget they think will help them achieve a goal, but they’ll have a monthly budget amount in the back of their mind. There’s no way to communicate the true budget goal to Google. Google simply assumes that the real goal is to spend 30.4 times the daily budget in any month.
Luckily, Google has written a great AdWords script to help reach budgets, wherein you set a monthly target, and the script updates the daily budget based on how much is left to be spent and how many days are left.
At Optmyzr, we made this script available in our patent-pending Enhanced Scripts library, and we even gave it the ability to use historical day-of-week patterns to allocate more budget to higher-potential days of the week — a feature that’s particularly useful in the last few days of the month, when Google may miss out on the fact that you typically spend relatively little on the weekend.
This preview from AdWords scripts shows how the script is able to detect historical day-of-week delivery patterns and use this to set an appropriate daily budget to ensure the full budget will be spent by the end of the month. Screen shot courtesy Google, August 2017.
While these tools help ensure you’re not underspending, we’re still left with the problem over overspending.
Solution #2: Don’t exceed your budgets
If you don’t make many changes in AdWords, you could use the daily budget to make sure you’re never spending too much for a campaign for the month. Remember, they will not bill you more than 30.4 times the daily budget amount for any month; they call this the monthly charging limit.
But advertisers reading posts on Search Engine Land are probably not content to use a set-it-and-forget-it approach. The moment you shift budgets between campaigns in an attempt to optimize, or the moment you use a script like the one above, the calculation of the monthly charging limit no longer matches your true monthly budget goal.
Furthermore, a well-managed account uses as much granularity as possible because it provides more control. So advanced advertisers likely use campaign-level budgets rather than one shared budget for the whole account. This means that the company goal of a certain monthly AdWords budget still has to be translated into separate campaign budgets.
Now, you could use Automated Rules to turn off campaigns when they spend too much, but these rules can only run once per day, so your ads could continue to accrue additional cost for 23 hours and 59 minutes after they hit their limit.
Account managers usually want to stay within a total monthly budget across their entire AdWords account, so they may build spreadsheets or tools to track how they are doing against their targets. Screen shot courtesy of Optmyzr, August 2017.
To address this, we created a free script that can pause all campaigns in an account when the monthly target budget for the account is reached. It’ll even re-enable all the campaigns it paused when the next month starts. When I created this script, I thought it’d also be useful to give advertisers the ability to set budgets at a lower level than the campaign. So I added budgets for ad groups, keywords and ads.
The use case this solves is when advertisers add new keywords to experimental campaigns because they fear these new keywords could take away too much budget from keywords that have been in the account awhile and whose performance is already well understood.
When these keywords eventually prove themselves, they have to be moved into the non-experimental campaign, and they lose history. With this script, an advertiser can simply add a new keyword or ad into the place it should eventually be and use a script to limit its budget until it has proven itself.
Solution #3: Maximize traffic and split budgets fairly for franchises
But could we use a script like the one mentioned above to enforce budgets at another level, like by location? At first that may seem like a bit of a stupid suggestion, because after all, campaigns have location targeting and a campaign budget, so aren’t budgets in AdWords today effectively location budgets? While that’s true, there is a scenario in which I believe this can still be useful.
Say you’re running ads for a franchise. Typically, you’ll set up separate campaigns for each location, each with its own budget. But in densely populated areas, you might have multiple franchises in the same city.
For example, in Mountain View alone, there are eight Subway sandwich shops. If each of those locations needs to get their fair share of PPC advertising dollars, you could use separate campaigns with ZIP code targeting.
But the problem is that as you get more granular with targeting, Google is not always able to know where a user is, and you end up losing traffic. In one of my own campaigns, I looked at postal code data for our ads when they show in Australia. There were 18 clicks with a postal code associated. But in that same day, there were 30 clicks from Australia.
So Google didn’t know enough about the location for these 12 clicks to associate them with a postal code. These 12 clicks would have been lost if I were targeting only postal codes, rather than broader entities like cities or regions. So, to get more volume and to reduce the number of campaigns you need to maintain, it’s often desirable to target broader locations, like cities or regions.
But as a franchise, if I run a campaign for all of Mountain View, it’s possible that one of the locations eats up 70 percent of the budget, leaving just a small amount for the remaining six locations. While it may make sense from a business perspective to spend more where there are more people, in a franchise model, there is often a contract that says every location will get its fair share of the ad spend.
So here’s a script that looks at location data for a campaign and lets you disable specific granular locations when they exceed a certain level of spend for the month.
// Limit Monthly Cost By Postal Codes in a Campaign | |
// | |
// Copyright 2017 - Optmyzr Inc - All Rights Reserved | |
// Visit www.optmyzr.com for more AdWords Scripts and PPC Management Tools and Reports | |
// | |
// | |
// Purpose of the script: | |
// --------------------- | |
// To allow you to set a broad location target to capture more traffic in a regionwhile at the same time | |
// letting you limit the monthly cost for locations within the target region. | |
// When a target budget is exceeded, the location's bid adjustment is set to -90%. | |
// -90% modifiers for locations included in this script's settings are reset to 0% bid modifier the next month. | |
// | |
// Example Use Case: | |
// ----------------- | |
// Run a campaign for multiple franchise locations in one city but limit the maximum cost for each zip code | |
// to ensure a more fair distribution of ad budget for the franchisees. | |
// | |
// | |
// 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. | |
// --------------------- | |
// UPDATE THESE SETTINGS | |
// --------------------- | |
// Change this to the name of the campaign you'd like to manage | |
// To manage multiple campaigns, create multiple versions of this script or contact support@optmyzr.com | |
CAMPAIGN_NAME_IS = "Optimization- New"; //The name of the campaign to manage. This is case sensitive | |
// Change this to a list of postal codes with a maximum budget for the month. | |
// These locations should be inside a targeted region but they don't have to be specifically added as targets. | |
// For example, if you target New York City, you do not have to specifically target zip code 10001 as this falls inside NYC. | |
// If you do NOT have the postal code as a specific target in the campaign, you will need to add its corresponding Google | |
// location targeting ID for the value of adWordsID. You can find the values for adWordsId here: https://goo.gl/2BXevL | |
// example: {"postalCode": "98040", "budget" : 100, "adWordsId" : "9033282"} | |
LOCATION_BUDGET_TARGETS = [ | |
{"postalCode": "98040", "budget" : 100, "adWordsId" : "9033282"}, | |
{"postalCode" : "94022", "budget" : 100, "adWordsId" : "9031912"}, | |
{"postalCode" : "75044", "budget" : 100, "adWordsId" : "9026815"} | |
]; | |
function main() { | |
// Go through the list of budgets set up in the script | |
Logger.log("These are the budgets you've specified in the settings for this script."); | |
Logger.log("LOCATION\t BUDGET"); | |
var locationsWithBudgets = new Array(); | |
var locationsWithAdWordsId = new Array(); | |
for (var i = 0; i < LOCATION_BUDGET_TARGETS.length; i++) { | |
var budgetLine = LOCATION_BUDGET_TARGETS[i]; | |
var location = budgetLine.postalCode; | |
var budgetAmount = budgetLine.budget; | |
var adWordsLocationId = budgetLine.adWordsId; | |
Logger.log(location + "\t\t " + budgetAmount); | |
locationsWithBudgets[location] = budgetAmount; | |
locationsWithAdWordsId[location] = adWordsLocationId; | |
} | |
Logger.log(""); | |
if(CAMPAIGN_NAME_IS.indexOf("'") != -1) { | |
var needle = '"' + CAMPAIGN_NAME_IS + '"'; | |
} else { | |
var needle = "'" + CAMPAIGN_NAME_IS + "'"; | |
} | |
// Not sure what locations your ad is showing in? Uncomment the following line of code to see your spend by location in a spreadsheet | |
// exportLocationsToSheet(); | |
evaluateLocationCosts(locationsWithBudgets, locationsWithAdWordsId, needle); | |
} | |
function getTargetedLocations(campaignName, locationsWithBudgets) { | |
var locationIds = new Array(); | |
var campaignIterator = AdWordsApp.campaigns() | |
.withCondition('Name = ' + campaignName) | |
.get(); | |
if (campaignIterator.hasNext()) { | |
var campaign = campaignIterator.next(); | |
var locationIterator = campaign.targeting().targetedLocations().get(); | |
while (locationIterator.hasNext()) { | |
var targetedLocation = locationIterator.next(); | |
var targetId = targetedLocation.getId(); | |
var entityType = targetedLocation.getEntityType(); | |
var locationName = targetedLocation.getName(); | |
if(locationsWithBudgets[locationName]) { | |
//Logger.log("this location has a budget"); | |
locationIds[locationName] = targetId; | |
} else { | |
//Logger.log("this targeted location doesn't have a budget in this script"); | |
} | |
/*Logger.log('Location name: ' + | |
targetedLocation.getName() + ', country code: ' + | |
targetedLocation.getCountryCode() + ', bid modifier: ' + | |
targetedLocation.getBidModifier() + " id: " + targetId + " entityType: " + entityType); | |
*/ | |
} | |
} | |
return locationIds; | |
} | |
function exportLocationsToSheet() { | |
var spreadsheet = SpreadsheetApp.create('GEO_PERFORMANCE_REPORT'); | |
var report = AdWordsApp.report( | |
'SELECT CampaignName, Clicks, Impressions, Cost, MostSpecificCriteriaId, CountryCriteriaId, IsTargetingLocation ' + | |
'FROM GEO_PERFORMANCE_REPORT ' + | |
'WHERE Impressions > 1 ' + | |
'AND CampaignName = "' + CAMPAIGN_NAME_IS + '" ' + | |
'DURING THIS_MONTH'); | |
report.exportToSheet(spreadsheet.getActiveSheet()); | |
Logger.log("Locations where your ad is running: " + spreadsheet.getUrl() + "\n"); | |
} | |
function evaluateLocationCosts(locationsWithBudgets, locationsWithAdWordsId, campaignName) { | |
var locationIds = getTargetedLocations(campaignName, locationsWithBudgets); | |
var campaignIterator = AdWordsApp.campaigns() | |
.withCondition('Name = ' + campaignName) | |
.get(); | |
if (campaignIterator.hasNext()) { | |
var campaign = campaignIterator.next(); | |
} | |
Logger.log("Evaluating costs for locations with a budget cap..."); | |
var report = AdWordsApp.report( | |
'SELECT CampaignName, Clicks, Impressions, Cost, MostSpecificCriteriaId, CountryCriteriaId, RegionCriteriaId, MetroCriteriaId, CityCriteriaId ' + | |
'FROM GEO_PERFORMANCE_REPORT ' + | |
'WHERE Impressions > 1 ' + | |
'AND CampaignName = "' + CAMPAIGN_NAME_IS + '" ' + | |
'DURING THIS_MONTH'); | |
var rows = report.rows(); | |
while(rows.hasNext()) { | |
var row = rows.next(); | |
var mostSpecific = row['MostSpecificCriteriaId']; | |
var cost = row['Cost']; | |
if(locationsWithBudgets[mostSpecific] > 0) { | |
if(cost < locationsWithBudgets[mostSpecific]) { | |
Logger.log(mostSpecific + ": $" + cost + " does NOT exceed budget."); | |
var locationId = locationIds[mostSpecific]; | |
if(locationId) { | |
//Logger.log("id: " + locationId); | |
var ids = [locationId]; | |
// Update an existing location target | |
var locationTargetIter = campaign.targeting().targetedLocations().withIds(ids).get(); | |
if(locationTargetIter.hasNext()) { | |
var locationTarget = locationTargetIter.next(); | |
var bidModifier = locationTarget.getBidModifier(); | |
if(bidModifier == 0.1) { | |
locationTarget.setBidModifier(1); | |
Logger.log(" Bid modifier removed"); | |
} else { | |
Logger.log(" Bid modifier exists but is not -90% so leaving as-is"); | |
} | |
} | |
} | |
} else { | |
Logger.log(mostSpecific + ": $" + cost + " EXCEEDS budget."); | |
var locationId = locationIds[mostSpecific]; | |
if(locationId) { | |
//Logger.log("id: " + locationId); | |
var ids = [locationId]; | |
// Update an existing location target | |
var locationTargetIter = campaign.targeting().targetedLocations().withIds(ids).get(); | |
if(locationTargetIter.hasNext()) { | |
var locationTarget = locationTargetIter.next(); | |
locationTarget.setBidModifier(0.1); | |
Logger.log(" Bid modifier set to -90%"); | |
} | |
} else { | |
// Create a new location target | |
var locationId = parseInt(locationsWithAdWordsId[mostSpecific]); | |
//Logger.log("user provided location ID: " + locationId); | |
campaign.addLocation({ | |
id: locationId, | |
bidModifier: 0.1, | |
}); | |
Logger.log(" Location target added and bid modifier set to -90%"); | |
} | |
} | |
} | |
} | |
} |
Conclusion
Budgets are not hard to understand, but they can be time-consuming and tedious to manage manually. Looking at whether daily budgets are dialed in to fully spend the target; ensuring an account hasn’t spent too much; and analyzing budget usage for granular locations are all well-defined tasks — all of this makes these tasks ideal for automation with AdWords scripts.
The post Advanced budget management made easy with scripts appeared first on Search Engine Land.
from SEO Rank Video Blog http://ift.tt/2wSGQFi
via IFTTT
No comments:
Post a Comment