August 2021 Update
Some nice people reached out to let me know that the 2019 script began failing once Google made some updates to their API. The below script should address those issues. The formatting makes it not very readable here, so if you prefer I’ve also posted it to Github.
//Author: Jake Ratliff, June 2019. Updated August 2021. var MONTHLY_BUDGET = 50; //change this to your monthly budget max var EXCLUDED_LABELS = '["branded"]' //you can add any labels you may want to exclude in future to this list. I only used "branded" as an example label. //the quotes around your label name are required, it is how this script can read your labels //for example: '["branded", "special campaign", "holiday sale", "geotarget USA"]' function main() { var itsFirstOfTheMonth = ((new Date()).getDate() == 1); //you can test this any time by setting to true var totalCostMTD = getTotalCost().toFixed(2); //you can test this at any spend level by setting to any number console.log("Total cost this month: $" + totalCostMTD + "; monthly budget: $" + MONTHLY_BUDGET ); if (totalCostMTD >= MONTHLY_BUDGET) { console.log("Total spend for campaigns not listed in EXCLUDED_LABELS has reached monthly budget"); applyLabel(); pauseCampaigns(); } else { console.log("Total spend for campaigns not listed in EXCLUDED_LABELS is under their monthly budget - no changes."); }; if (itsFirstOfTheMonth) { reenableCampaigns(); }; }; function getTotalCost() { var campIter = AdsApp.campaigns() .withCondition('LabelNames CONTAINS_NONE ' + EXCLUDED_LABELS) .get(); var totalCost = 0; while (campIter.hasNext()) { totalCost += campIter.next().getStatsFor("THIS_MONTH").getCost(); }; return totalCost; }; function labelExists(labelToCheck) { var labelIterator = AdsApp.labels().get(); console.log(labelIterator); while (labelIterator.hasNext()) { var label = labelIterator.next(); console.log(label.getName()) if (label == labelToCheck) { return true } else { return false } } } function getAccountLabelNames() { var labelNames = []; var iterator = AdsApp.labels().get(); while (iterator.hasNext()) { label = iterator.next().getName(); console.log(label) labelNames.push(label); } return labelNames; } function applyLabel() { var labelName = 'Paused by Budget Script'; var existingLabels = getAccountLabelNames(); if (existingLabels.indexOf(labelName) == -1) { AdsApp.createLabel(labelName); } var campaignIterator = AdsApp.campaigns() .withCondition('CampaignStatus = ENABLED') .withCondition('LabelNames CONTAINS_NONE ' + EXCLUDED_LABELS) .get(); while (campaignIterator.hasNext()) { var campaign = campaignIterator.next(); console.log("label " + labelName + " applied to " + campaign) campaign.applyLabel(labelName); }; console.log('labels applied.'); }; function pauseCampaigns() { var campaignIterator = AdsApp.campaigns() .withCondition('CampaignStatus = ENABLED') .withCondition('LabelNames CONTAINS_NONE ' + EXCLUDED_LABELS) .get(); while (campaignIterator.hasNext()) { var campaign = campaignIterator.next(); campaign.pause(); }; console.log('Campaigns not listed in EXCLUDED_LABELS paused'); }; function reenableCampaigns() { var label = AdsApp.labels() .withCondition('Name = "Paused by Budget Script"') .get().next(); var campaignIterator = label.campaigns().get(); while (campaignIterator.hasNext()) { var campaign = campaignIterator.next(); campaign.removeLabel('Paused by Budget Script'); campaign.enable(); }; console.log('First of the month: campaigns reenabled') };
June 2019 Update
Someone reached out to me on LinkedIn today and said that they are using an Adwords script I wrote but they would like to not apply it to some campaigns. I thought that was a good idea and I wrote up an updated version that allows for just that. If you don’t need that, the original script automatically pauses all active campaigns once ad spend reaches your predefined limit. Then, on the first day of the next month, it unpauses only the campaigns that it paused in the previous month. The original script is here.
Here is the updated version:
//Author: Jake Ratliff, June 2019 var MONTHLY_BUDGET = 50; //change this to your monthly budget max var EXCLUDED_LABELS = '["branded"]' //you can add other labels you may want to exclude in future to this list //for example: '["branded", "special campaign", "holiday sale", "geotarget USA"]' function main() { var itsFirstOfTheMonth = ((new Date()).getDate() == 1); var totalCostMTD = getTotalCost().toFixed(2); Logger.log("Total cost this month: $" + totalCostMTD + "; monthly budget: $" + MONTHLY_BUDGET ); if (totalCostMTD >= MONTHLY_BUDGET) { Logger.log("Total spend for campaigns not listed in EXCLUDED_LABELS has reached monthly budget"); applyLabel(); pauseCampaigns(); }else{ Logger.log("Total spend for campaigns not listed in EXCLUDED_LABELS is under their monthly budget - no changes."); }; if (itsFirstOfTheMonth) { reenableCampaigns(); }; }; function getTotalCost() { var campIter = AdWordsApp.campaigns() .withCondition('LabelNames CONTAINS_NONE ' + EXCLUDED_LABELS) .get(); var totalCost = 0; while (campIter.hasNext()) { totalCost += campIter.next().getStatsFor("THIS_MONTH").getCost(); }; return totalCost; }; function labelExists(labelToCheck) { var labelIterator = AdsApp.labels().get(); Logger.log(labelIterator); while (labelIterator.hasNext()) { var label = labelIterator.next(); Logger.log(label.getName()) if(label == labelToCheck){ return true }else{ return false } } } function getAccountLabelNames() { var labelNames = []; var iterator = AdsApp.labels().get(); while (iterator.hasNext()) { labelNames.push(iterator.next().getName()); Logger.log(""); } return labelNames; } function applyLabel() { var labelName = 'Paused by Budget Script'; var existingLabels = getAccountLabelNames(); if (existingLabels.indexOf(labelName) == -1){ AdWordsApp.createLabel(labelName); } var campaignIterator = AdWordsApp.campaigns() .withCondition('CampaignStatus = ENABLED') .withCondition('LabelNames CONTAINS_NONE ' + EXCLUDED_LABELS) .get(); while (campaignIterator.hasNext()) { var campaign = campaignIterator.next(); Logger.log("label " + labelName + " applied to " + campaign) campaign.applyLabel(labelName); }; Logger.log('labels applied.'); }; function pauseCampaigns() { var campaignIterator = AdWordsApp.campaigns() .withCondition('CampaignStatus = ENABLED') .withCondition('LabelNames CONTAINS_NONE ' + EXCLUDED_LABELS) .get(); while (campaignIterator.hasNext()) { var campaign = campaignIterator.next(); campaign.pause(); }; Logger.log('Campaigns not listed in EXCLUDED_LABELS paused'); }; function reenableCampaigns() { var label = AdWordsApp.labels() .withCondition('Name = "Paused by Budget Script"') .get().next(); var campaignIterator = label.campaigns().get(); while (campaignIterator.hasNext()) { var campaign = campaignIterator.next(); campaign.removeLabel('Paused by Budget Script'); campaign.enable(); }; Logger.log('First of the month: campaigns reenabled') };
Original Monthly Spend Script
I want to share this Adwords script I wrote a few months ago because I thought it might help some account managers who were having a similar problem. Adwords allows you to set daily budgets for your campaigns, but there is not a way to set a monthly budget.
There are some features that come close, but in my opinion don’t quite do the trick. For example, there is Manager Defined Spend (MDS) but that is only useful for agencies that manage multiple accounts, and even then it might not be the ideal method. The shared budgets feature lets you set monthly limits for campaigns that share a budget, but what if you don’t want to use a shared budget? Nine times out of ten you will want to allot varied daily budgets to your campaigns, so that you can reward the high converters with a greater share of the budget.
I wrote the script below to add what I consider to be a basic feature to Adwords. With this bit of code, you simply provide your monthly budget and Adwords will pause all active campaigns in the account if their total spend month-to-date meets that number. Then, on the first day of the next month, it will enable those campaigns once again.
To use this script, copy and paste it into your Adwords account under Bulk Operations >> Scripts >> New Script.
//Author: Jake Ratliff //April 14, 2016 var MONTHLY_BUDGET = YOUR_BUDGET_GOES_HERE; //NOTE: set MONTHLY_BUDGET to a number slightly less than //your actual monthly budget and set this script to run //hourly. Setting this variable to less than actual budget //will keep you from going over between hours. //This is the main function, which Adwords calls when the //script is run, so it must be named main. In our main //function we are logging the total cost month-to-date, //checking if that number is greater than the specified //budget, and if it is, we apply a label to all active //campaigns and then pause all active campaigns. Finally //we check if it is the first day of the month, and if it //is, we re-enable all the campaigns that have the label //that we applied earlier. function main() { var itsFirstOfTheMonth = ((new Date()).getDate() == 1); var totalCostMTD = getTotalCost().toFixed(2); Logger.log("Total cost this month: $" + totalCostMTD + "; monthly budget: $" + MONTHLY_BUDGET ); if (totalCostMTD >= MONTHLY_BUDGET) { Logger.log("spend has reached monthly budget"); applyLabel(); pauseCampaigns(); }; if (itsFirstOfTheMonth) { reenableCampaigns(); }; }; function getTotalCost() { var campIter = AdWordsApp.campaigns().get(); var totalCost = 0; while (campIter.hasNext()) { totalCost += campIter.next().getStatsFor("THIS_MONTH").getCost(); }; return totalCost; }; function applyLabel() { var labelName = 'Active Last Month'; AdWordsApp.createLabel(labelName); var campaignIterator = AdWordsApp.campaigns() .withCondition('CampaignStatus = ENABLED') .get(); while (campaignIterator.hasNext()) { var campaign = campaignIterator.next(); campaign.applyLabel(labelName); }; Logger.log('labels applied.'); }; function pauseCampaigns() { var campaignIterator = AdWordsApp.campaigns() .withCondition('CampaignStatus = ENABLED') .get(); while (campaignIterator.hasNext()) { var campaign = campaignIterator.next(); campaign.pause(); }; Logger.log('enabled campaigns paused'); }; function reenableCampaigns() { var label = AdWordsApp.labels() .withCondition('Name = "Active Last Month"') .get().next(); var campaignIterator = label.campaigns().get(); while (campaignIterator.hasNext()) { var campaign = campaignIterator.next(); campaign.removeLabel('Active Last Month'); campaign.enable(); }; Logger.log('First of the month: campaigns reenabled') };
15 replies on “Adwords Script for Limiting Monthly Spend”
Hi Jake Ratliff,
I am using your script for my couple of accounts and that works well till this month, but Google will come up some new updates on the google ads API and with the new BETA version, this script won’t work, If you can share the new updated script in the new article would be appreciated and very helpful.
Thanks for letting me know about the update, Vanrajsinh. I will update the script this week.
Hi, Vanrajsinh. Could you show me the error you’re seeing? It would be in the logs where the script runs.
Hi Vanrajsinh, I’ve posted an updated version of the script. It is also in Github here: https://github.com/JakeRatliff/adwords_scripts/blob/main/aw_script2.js
Let me know how it goes!
Thank you Jake Ratliff,
I just go through your new script, i will try your new script and update you shortly if found any issue. thanks again.
I loved this awesome script for a long time – but now some of them are failing with the new API. So frustrating! Can’t wait to check back and see the new one you come up with. This is my favorite script THANK YOU!
Hi, Beverly. Could you show me the error you’re seeing? It would be und logs where the script runs.
That’s the surprising thing – there is no error. It runs “successfully” but doesn’t apply the label or pause. I tested it over and over again – different accounts. It SHOULD be working. The interesting thing is, I use both of your scripts – the Original as well as the one with Excluded Labels. It the Excluded Labels one that I am noticing not pausing out. I just checked another one that uses the original today and no problem – it is paused. At the very least I can replace with the Original script and just set a rule for labels for now if I have to (Google Ad rules are sooo terrible). Why Google, why??! I wish I were better at scripts!!
Thanks Beverly, that is helpful info. I’m looking into this some more.
Hi Beverly, I’ve posted an updated version of the script. It is also in Github here: https://github.com/JakeRatliff/adwords_scripts/blob/main/aw_script2.js
Let me know how it goes!
Update works great! For some reason it kept failing at first when I updated it, so I just disabled the old script and started a “fresh” one and it ran fine. Thanks!!
You’re welcome! Thanks for letting me know about the issue and confirming its fixed.
Hey Jake! My man! Currently the script pauses out for all campaigns in the account but it would be great since the launch of local service ads if the campaigns would pause out based on the total account spend. Since LSAs are not considered “campaigns” is there a way to accomplish this? I know there’s no way to pause out the LSAs, but the LSA spend is now part of the total spend of the account which includes the campaigns. I’ve tried like hell to mod your script but can’t figure it out.
Another option I’d like to ask is if there’s a modification we can do – instead of pausing out, can we lower the daily budget to $1.00 instead and have the option to ramp it back up on the start of the month back to what the monthly budget is set to in the script?
I am happy to pay $$ for either of these solutions.
This is such a well written script that for it to continue to run and work into 2024 is so badass. Is there a way to calculate and pause campaigns based ONLY on the selected/labeled campaigns and NOT the TOTAL spend for the account? Local Services Ads spend is now included on the account spend total, but of course LSAs can’t run scripts or rules and there’s no way to opt out of the “campaign” of LSAs. Instead is there a way to use this script to ONLY include the total spend of the selected/labeled campaigns and pause/enable those ones?
You’re a genius, this script is amazing – I want to give you $$$ – please contact me 🙂
Hello Bev, thanks 🙂 I haven’t checked the comments here in a little while, thanks for your patience. I sent you an email.