Flexible Budget Script
The Flexible Budget Script was created to help the SEM department manage their account budgets throughout the month. There are two different functions within the Budget Script to help each account reach its full monthly spend.
Calculate Budget With Weight Function
How does it work?
This function has a 0-15 numerical scale built in to signal how heavy you need the account to spend. Each number on the scale represents a 20% increase in your campaigns normal daily budget. The first hour of the first day will start with this 20% increase and will slowly decrease throughout the day depending on how much is spent each hour. This 20% starting point for each day will fluctuate depending on how much has already been spent as the days go on. The idea is that the script will set the budgets higher than needed with an ultimate goal of reaching your campaign's daily budget by the end of the day.
What else can it do?
- If the campaign reaches its full budget, the script will automatically change the daily budget to $0.01 so that it will not continue spending. This will be automatically reset on the 1st day of the new month with the campaign's new budget.
- If your campaign reaches its full budget and changes to $0.01, you will receive an email every hour that it runs to inform you of this.
- If the script is not able to perform how it planned throughout the month, it is going to automatically give the campaigns a very large increase on the last two days of the month to try and spend the full budget.
Scale:
0: ~80% of full budget: The budget may start slightly higher but will end at 80% of the campaign's budget by the end of the day resulting in a slight underspend.
1: ~100% of full budget: This weight is intended to spend your campaign's budget evenly throughout the month.
2: ~120%
3: ~140%
4: ~160%
5: ~180%
6: ~200%
7: ~220%
8: ~240%
9: ~260%
10: ~280%
11: ~300%
12: ~320%
13: ~340%
14: ~360%
15: ~380%: This weight is intended for campaigns with consistently extreme underspend issues.
Calculate Budget With Extremely Heavy Spend For Issues Function
How does it work?
This function spends extremely heavily throughout the entire month. It should be used on accounts that have had historically ongoing, massive underspend issues where the goal is to try to spend most of the budget early on in the month so that you are not trying to catch up on huge amounts of spend at the very end of the month. It can also be used as a last resort tactic if you need your account to catch up on spend at the end of the month. The percentage increase depends on the total budget, how many days are left in the month, and how much has already been spent.
What else can it do?
- If the campaign reaches its full budget, the script will automatically change the daily budget to $0.01 so that it will not continue spending. This will be automatically reset on the 1st day of the new month with the campaign's new budget.
- If your campaign reaches its full budget and changes to $0.01, you will receive an email every hour that it runs to inform you of this.
- If the script is not able to perform how it planned throughout the month, it is going to automatically give the campaigns a very large increase on the last two days of the month to try and spend the full budget.
1. Open up your Google Ads account.
2. Click "Tools" and then "Scripts."
3. Click the "+" sign to add a new script.
4. Make sure the text box is empty and paste the following script:
// The Total Budget to be Entered
var TOTAL_BUDGET = 210;
// The name of the Campaign
var CAMPAIGN_NAME = ['General, Branded, Competition'];
// email address of the Rep
var REP_EMAIL_ADDRESS = 'bri.barzola@dealerspike.com';
// Mark TEST_BUDGET true for tests, mark false to go live
var TEST_BUDGET = true;
// Below is the list of various budget functions for different campaign functionality
// calculateBudgetWithWeight --> With the variable (BUDGET_FUNCTION_WEIGHT) below, you can select how fast the budget will spend over the month from 120 percent to 400 percent starting budgets. Warning, the higher the weight, the quicker it will spend the budget meaning less budget at end of the month
// calculateBudgetWithExtremelyHeavySpendForIssues --> Spends heavy all month til last day but still tries to spend heavily earlier on the last day if the budget is still higher.
// Choose one to run ^^^^^^^
var BUDGET_FUNCTION = calculateBudgetWithWeight;
// The Budget Function weight is how heavy you need the account to currently spend. 0 is very close to a even budget. It goes up to 15 which can go up to over 4 times the normal budget to start then
var BUDGET_FUNCTION_WEIGHT = 0;
// ======================================================================================================== //
// Main Function here. Will check to see which script to run, Live or Test
function main() {
if (TEST_BUDGET) {
testBudgetStrategy(BUDGET_FUNCTION, 31, TOTAL_BUDGET);
} else {
setNewBudget(BUDGET_FUNCTION, TOTAL_BUDGET);
}
}
// Live Version of the Update Budget Function
function setNewBudget(budgetFunction, totalBudget) {
// Get the Date Data
var currentFullDate = new Date();
var lastDayofMonth = new Date(
currentFullDate.getFullYear(),
currentFullDate.getMonth() + 1,
0
);
var start = new Date(getMonthText(currentFullDate.getMonth()) + ' 1, ' + currentFullDate.getFullYear() +
' 0:00:00 -0800');
var end = new Date(getMonthText(currentFullDate.getMonth()) + ' ' + lastDayofMonth.getDate() + ', ' +
currentFullDate.getFullYear() + ' 0:00:00 -0800');
var startDateShort = currentFullDate.getFullYear().toString() + getMonthShortNum(currentFullDate.getMonth()) + '01';
var endDateShort = currentFullDate.getFullYear().toString() + getMonthShortNum(currentFullDate.getMonth()) + lastDayofMonth.getDate().toString();
// Run the Script after ensuring it needs to run
var today = new Date();
var costSoFar = 0.00;
if (today < start) {
Logger.log('Not ready to set budget yet');
return;
};
for (var index = 0; index < CAMPAIGN_NAME.length; index++) {
var campaign = getCampaign(CAMPAIGN_NAME[index]);
costSoFar += campaign.getStatsFor(getDateStringInTimeZone(startDateShort, start), getDateStringInTimeZone(endDateShort, end)).getCost();
};
var daysSoFar = datediff(start, today);
var totalDays = datediff(start, end) + 1;
Logger.log('Start Date: %s | End Date: %s | Today Date: %s', start, end, today);
Logger.log('Days So Far: %s | Total Days: %s | Cost So Far: %s', daysSoFar, totalDays, costSoFar);
var newBudget = budgetFunction(costSoFar, totalBudget, daysSoFar, totalDays);
if (newBudget == 0.01){
campaign.getBudget().setAmount(newBudget);
MailApp.sendEmail(REP_EMAIL_ADDRESS, CAMPAIGN_NAME.toString() + " Monthly Budget Met for Campaign. "+ CAMPAIGN_NAME + " have been reduced to 0.01 per day until new month hits. This is an auto generated email from the Adwords Script made by the Marketing Development Team in the SEM account.");
} else {
campaign.getBudget().setAmount(newBudget);
}
}
// Test Version of the Update Budget Function
function testBudgetStrategy(budgetFunc, totalDays, totalBudget) {
var daysSoFar = 0;
var costSoFar = 0;
while (daysSoFar < totalDays) {
var newBudget = budgetFunc(costSoFar, totalBudget, daysSoFar, totalDays);
Logger.log('Day %s of %s, new budget %s, cost so far %s', daysSoFar + 1,
totalDays, newBudget, costSoFar);
costSoFar += newBudget;
daysSoFar += 1;
}
}
// Helper Functions - Details with each below
// Gets the Month Text from input of the month number
function getMonthText(monthNumber) {
var monthList = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
return monthList[monthNumber];
}
// Gets the two digit number for a month given the month number in the Date function
function getMonthShortNum(monthNum) {
var monthNumList = ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"];
return monthNumList[monthNum];
}
//Returns number of days between two dates, rounded up to nearest whole day.
function datediff(from, to) {
var millisPerDay = 1000 * 60 * 60 * 24;
return Math.ceil((to - from) / millisPerDay);
}
// Formats the Date String the in the correct Time Zone
function getDateStringInTimeZone(format, date, timeZone) {
date = date || new Date();
timeZone = timeZone || AdWordsApp.currentAccount().getTimeZone();
return Utilities.formatDate(date, timeZone, format);
}
// Finds a campaign by name, whether it is a regular, video, or shopping campaign, by trying all in sequence until it finds one. Takes in the campaign name
function getCampaign(campaignName) {
var selectors = [AdWordsApp.campaigns(), AdWordsApp.videoCampaigns(),
AdWordsApp.shoppingCampaigns()
];
for (var i = 0; i < selectors.length; i++) {
var campaignIter = selectors[i].
withCondition('CampaignName = "' + campaignName + '"').
get();
if (campaignIter.hasNext()) {
return campaignIter.next();
}
}
throw new Error('Could not find specified campaign: ' + campaignName);
}
// calculateBudgetWithExtremelyHeavySpendForIssues --> Spends heavy all month til last day but still tries to spend heavily eariler on the last day if the budget is still higher.
function calculateBudgetWithExtremelyHeavySpendForIssues(costSoFar, totalBudget, daysSoFar, totalDays) {
var remainingDays = totalDays - daysSoFar;
if (remainingDays == 0){
remainingDays = 1;
}
var remainingTotalBudget = totalBudget - costSoFar;
var baseBudgetAmount = totalBudget / totalDays;
var baseReturnBudget = remainingTotalBudget / remainingDays;
if(remainingTotalBudget < 1.00){
return 0.01;
}
else if(remainingDays <= 1)
{
if((baseBudgetAmount/2) > remainingTotalBudget){
return baseBudgetAmount;
} else {
return (baseBudgetAmount < remainingTotalBudget) ? baseBudgetAmount * 3 : remainingTotalBudget * 3;
}
} else {
if(baseReturnBudget < baseBudgetAmount) {
return baseBudgetAmount * 2;
} else {
return baseReturnBudget * 3;
}
}
}
// calculateBudgetWithWeight --> With the variable (BUDGET_FUNCTION_WEIGHT) below, you can select how fast the budget will spend over the month from 10.0 percent to 400 percent starting budgets. Warning, the higher the weight, the quicker it will spend the budget meaning less budget at end of the month
function calculateBudgetWithWeight(costSoFar, totalBudget, daysSoFar, totalDays) {
var remainingDays = totalDays - daysSoFar;
if (remainingDays == 0){
remainingDays = 1;
}
var remainingTotalBudget = totalBudget - costSoFar;
var baseBudgetAmount = totalBudget / totalDays;
var baseReturnBudget = remainingTotalBudget / remainingDays;
var dayWeight = BUDGET_FUNCTION_WEIGHT / daysSoFar;
if(remainingTotalBudget < 1.00){
return 0.01;
}
else if (BUDGET_FUNCTION_WEIGHT == 0){
return (baseBudgetAmount > baseReturnBudget) ? baseBudgetAmount : baseReturnBudget;
}
else if (remainingDays <= 2) {
if (remainingDays <= 1) {
if(remainingTotalBudget > baseBudgetAmount){
return remainingTotalBudget * (1 + (0.1 * BUDGET_FUNCTION_WEIGHT));
} else {
if((remainingTotalBudget / 2) < baseBudgetAmount) {
return baseBudgetAmount;
} else {
return baseBudgetAmount * (1 + (0.05 * BUDGET_FUNCTION_WEIGHT));
}
}
} else {
if(baseReturnBudget > baseBudgetAmount) {
return remainingTotalBudget * (1 + (0.2 * BUDGET_FUNCTION_WEIGHT));
} else {
if(baseReturnBudget < (baseBudgetAmount / 2)) {
return (baseBudgetAmount * (2/3));
} else {
return (baseBudgetAmount * (3/2));
}
}
}
} else {
if (baseReturnBudget >= baseBudgetAmount){
return (baseReturnBudget * (1 + (0.2 * BUDGET_FUNCTION_WEIGHT)));
} else {
return (baseReturnBudget + ((baseBudgetAmount - baseReturnBudget) * dayWeight));
}
}
}
5. Name your script "Flexible Budget Script" and also include the campaign name(s)/budget that you are applying it to.
Examples:
"Flexible Budget Script - Yamaha Co-Op"
"Flexible Budget Script - Shared Budget"
6. Next to "TOTAL_BUDGET =" insert your campaign's monthly budget. If you are applying the script to multiple campaigns on a shared budget, insert the campaigns' shared monthly budget. Make sure that you are inserting the ACTUAL SPEND, NOT the contracted spend.
7. Next to "CAMPAIGN_NAME =" insert your campaign name(s). If you are applying the script to multiple campaigns on a shared budget, insert the campaign names of each individual campaign separated by a comma within the quotations.
Example:
CAMPAIGN_NAME = ['General, Branded, Competition'];
Note: The campaign names inserted in the script need to identically match up to the actual campaign names.
8. Next to "REP_EMAIL_ADDRESS =" replace "bri.barzola@dealerspike.com" with your own email address.
9. Next to "TEST_BUDGET =" please do one of the following:
If you are testing out the script but do not want it to actually run live on your campaigns, insert "true".
If you want the script to run on your campaigns, insert "false".
10. Next to "BUDGET_FUNCTION =" insert the function that you would like to use, which can be copied from lines 11 and 12 in the script:
calculateBudgetWithWeight
calculateBudgetWithExtremelyHeavySpendForIssues
11. If you are using the "Calculate Budget With Weight" function, insert the numerical scale that you would like the script to run on next to "BUDGET_FUNCTION_WEIGHT =". This should be a number between 0-15.
Note: For definitions on each numbered scale, refer back to the "Calculate Budget With Weight Function" slide.
12. Authorize your script.
13. Click "Save" then "Close".
14. Back in the "Scripts" panel, make sure to change the frequency of your script to "HOURLY."
15. Click back into the script and select "Preview".
16. In the "Logs" tab you will be able to see how the campaigns budget will be adjusted each hour.
17. Once you have successfully completed the setup click "Run" and "Close".
Monitoring The Script
Throughout the month you will continue to monitor your budgets based on the actions of the script and will need to adjust the script as needed. Below are a few examples of changes you will need to make to your script throughout the month:
If your campaign is not making much progress on spend, increase the scale and continue to keep an eye on it.
If your account starts to overspend, decrease the scale and continue to keep an eye on it.
If you are using the "Calculate Budget With Weight" function but are in a time sensitive situation and you are not seeing any improvement, switch to the "Calculate Budget With Extremely Heavy Spend For Issues" function.