Monday, March 18, 2013

Dynamically Adjust Campaign Budgets

UPDATE 2013-04-05: There is an updated version of this script. Check it out. Dynamically Adjust Campaign Budgets v2.0.

UPDATE 2013-04-07: A big thank you to FoxSUP for helping me track down an issue with updating the budgets. Fixed line 78 to multiply the current budget by 1+to_change instead of just to_change. Also fixed a bug in calculating the change (line 76).

Here is a request from a reader:
I manage many small business PPC accounts, and some of these accounts have multiple campaigns, and they usually have a relatively small monthly click budget. I'm looking for a way to pause ALL campaigns if the entire account has spent over a certain amount month-to-date.

This is actually a pretty easy script to put together. Below is a script that will do just that. You can set the MONTHLY_BUDGET at the beginning of the script and run this script daily on your account. Once the campaigns have a total cost greater than the budget specified, it will pause all the campaigns in the account.

Then, on the first of the next month, it will enable those campaigns once again. If you make no changes to the script below, it should do just that. But let's go one step further.

You actually have the power to get and set your campaign budgets using scripts. So let's say your monthly budget is $100, but you want to make sure your ads are spaced out through the month. I have added a function below called _adjust_campaign_budget() which can be enabled through the flag ADJUST_BUDGETS at the top of the script.

The script will then attempt to calculate a run rate for your campaign to figure out if you are going to meet your budget or not. If you are going to go over, it will lower the budget of each campaign (weighted by campaign cost) so that you come in at your target. If you are going to under-spend, it will also attempt to increase your campaign budget to try to allow you to hit your goal. I have also added a _reset_budgets() function to the end that gets called on the first of the month. If you run this script more frequently, you should enable the code for checking if it is the first hour of the first day of the month.

Now understandably, this script comes with a few cautions. THIS SCRIPT MAY CAUSE YOU TO SPEND A LOT OF MONEY. I'm sure the campaigns I was testing this on were quite a bit smaller than your campaigns, with budgets to match.


* Keep Your Campaigns In Budget
* Version 1.1
* ChangeLog v1.1 
*   - cleaned up code
*   - added ability for any dates
* Created By: Russ Savage
// Let's set some constants
var MONTHLY_BUDGET = 5000.00;

//If you want to work with a monthly budget, leave START_DATE and END_DATE blank.
//But if you want to work with a specific timeframe, fill these in.
//Use the format yyyyMMdd, so for Jan 12th, 2014, you would put 20140112.
var START_DATE = '';
var END_DATE = '';
//Set this to true if you want to adjust budgets or
//keep set to false if you want to just pause all the campaigns
//when you hit your budget
var ADJUST_BUDGETS = false;
function main() {
  var totalCostMTD = getTotalCost();
  var isFirstOfTheMonth = ((new Date()).getDate() == 1);
    var today = new Date();
    var startDate = new Date(START_DATE.substring(0,4),
    isFirstOfTheMonth = (startDate.getTime() == today.getTime());
  //if you run this script more than once per day, uncomment the next line
  //isFirstOfTheMonth = (isFirstOfTheMonth && ((new Date()).getHour() == 0));
  Logger.log("Total cost: " + totalCostMTD + 
           ", Monthly budget:" + MONTHLY_BUDGET +
           ", isFirstOfTheMonth: "+isFirstOfTheMonth);
    if(isFirstOfTheMonth) {
    } else {
  } else {
    if(totalCostMTD >= MONTHLY_BUDGET) {
      //If we have hit the limit, pause all ads
    } else {
      // let's check if it's the first day of the month
      if((new Date()).getDate() == 1) {
        //enable all the campaigns
// Returns the total cost for the set TIMEFRAME
function getTotalCost() {
  var campIter = AdWordsApp.campaigns().get();
  var totalCost = 0;
  while(campIter.hasNext()) {
    if(START_DATE && END_DATE) {
      totalCost +=,END_DATE).getCost();
    } else {
      totalCost +=;
  return totalCost;
// Enables or Disables All Campaigns In Account
function enableOrDisableCampaigns(shouldDisable) {
  var campIter = AdWordsApp.campaigns().get();
  while(campIter.hasNext()) { 
    if(shouldDisable) {; 
    } else {; 
// Calculates run rate and adjusts campaign bids as needed.
function adjustCampaignBudget(myTotalCost) {
  var today = new Date();
  // Accounting for December
  var eom;
    eom = new Date(END_DATE.substring(0,4),
  } else {
    eom = (today.getMonth() == 11) ? new Date(today.getFullYear()+1,0,1) : 
                                     new Date(today.getFullYear(),today.getMonth()+1,1);
  var daysLeft = Math.round((eom-today)/1000/60/60/24);
  var daysSpent;
    var startDate = new Date(START_DATE.substring(0,4),
    daysSpent = Math.round((today-startDate)/1000/60/60/24);
  } else {
    daysSpent = today.getDate();
  var runRate = round(myTotalCost/daysSpent);
  var projectedTotal = myTotalCost + (runRate * daysLeft);
  var percOver = round((MONTHLY_BUDGET-projectedTotal)/projectedTotal);
//Adjusts the budget for a given campaign based on percentage of total spend
//Note: if the cost of a campaign is $0 mtd, the budget is not changed.
function changeSpend(percToChange,myTotalCost) {
  var campIter = AdWordsApp.campaigns().withCondition("Status = ENABLED").get();
  while(campIter.hasNext()) {
    var camp =;
    var campCost = (START_DATE && END_DATE) ? camp.getStatsFor(START_DATE,END_DATE).getCost()
                                            : camp.getStatsFor(TIMEFRAME).getCost();
    var percOfTotal = round(campCost/myTotalCost);
    //If there is no cost for the campaign, let's not change it.
    var toChange = (percOfTotal) ? (percOfTotal*percToChange) : 0;
// Resets the budget evenly across all campaigns
function resetBudgets() {
  Logger.log('Resetting budgets at the first of the period.');
  var campIter = AdWordsApp.campaigns().withCondition("Status = ENABLED").get();
  var campCount = 0;
  while(campIter.hasNext()) {
  campIter = AdWordsApp.campaigns().withCondition("Status = ENABLED").get();
  while(campIter.hasNext()) {;

// A helper function to make rounding a little easier
function round(value) {
  var decimals = Math.pow(10,DECIMAL_PLACES);
  return Math.round(value*decimals)/decimals;


  1. Excellent script... I was about half way done with something very similar, and this post helped to resolve few obstacles...Thanks!

  2. Thanks again for the script. In regards to the campaigns reactivating on the first of the month, would this script enable every campaign that is currently in a "paused" state in the account, whether or not a campaign was enabled the prior month?

    If so, which lines of the code should be removed? Personally, I'm fine with going back into an acct on the first of the month and manually reactivating specific campaigns.


    1. Hi Mark, You are correct, this script will enable all paused campaigns in the account. Probably the easiest way to only run this script on certain campaigns would be to use labels. So let's assume you create a label called 'script' and apply it to the campaigns you want to manage the budget for.

      Then, anywhere in the script where you see a camp_iter (lines 45, 56, 84, and 98), add the following .withCondition("LabelNames CONTAINS_ANY ['script']") just before the .get();

      var camp_iter = AdWordsApp.campaigns()
      .withCondition("Status = ENABLED")
      .withCondition("LabelNames CONTAINS_ANY ['script']")

      That way the script will only operate on certain campaigns.


  3. I love this script, I set it up with the labels to control individual campaign budgets and its working great.

    I have also tried it with all campaigns, but I like to spend different amounts on different campaigns and it does not keep those values. Any ideas on how best to achieve this?

    Thanks for your time and effort posting useful AdWords Scripts.

    1. Hi FoxSUP, Yeah, right now, the script just simply divides your monthly budget equally across all your campaigns (line 106).

      There are a few different ways you could solve the budget thing. I think the easiest way might be to store the campaign information in an external spreadsheet and read those values at the start of each month. That way, you could adjust budgets without changing the script.

      The change to this was pretty substantial so I put it in a separate post: Dynamically Adjust Campaign Budgets v2.0.


    2. This comment has been removed by a blog administrator.

    3. Hi FoxSUP, I made a small change to multiple the budgets by 30.5 to handle the daily to monthly issue.


  4. For anyone adjusting budgets using this script, I fixed a bug that should adjust the campaign budgets correctly. A big thanks to FoxSUP for helping me track down some issues.


  5. Hi Russel,

    I enjoyed all the scripts on your blog and i found them very help full for my MCC and i have to thank you for this.

    I have a question witch i think it apply to this script: It's there a way to see in a spreadsheet the total budget and the cost of all the campaigns in an account?
    The goal is to correlate the dates between total cost and the budget for last 30 days or last 7 days (weekly).


    1. Hi Dany, I think you might be better off using this script to store that data independently: Store Account Performance Report in a Google Doc. Let me know if that solves your problem.


    2. Hi Russel,

      I looked up for that script but it seems i don't have the possibility to extract the budget from the ACCOUNT_PERFORMANCE_REPORT when i put 'Budget' in the var columns.

      "Column 'Budget' is not valid for report type ACCOUNT_PERFORMANCE_REPORT. Double-check your SELECT clause. (line 33) "

      My goal is to see if the Cost is reaching he's Budget.


    3. Hi Dany, You're correct, for some reason, that report doesn't work. The one you want is BUDGET_PERFORMANCE_REPORT. I tried to make the other script generic enough to use with most of the reports available from AdWords. To update the other script, all you need to do it replace ACCOUNT_PERFORMANCE_REPORT with BUDGET_PERFORMANCE_REPORT, update the column list using the values here: BUDGET_PERFORMANCE_REPORT columns, and you should be good to go.

      Let me know if that works for you.

    4. Hi Russel,

      Thanks man, the BUDGET_PERFORMANCE_REPORT works ok, and it is that one was i looking for but the thing is (problem) it imports in docs all the campaigns even if it is deleted or paused.
      Any tips to import only the enable campaings?

      My script looks like this:

      // Store Budget Performance Report In A Google Doc
      // Created By: Russ Savage
      function main() {
      var spreadsheet_url = "spreadsheet_url";
      var date_range = 'LAST_WEEK';
      var columns = ['AssociatedCampaignName',
      var columns_str = columns.join(',') + " ";

      var sheet = getSpreadsheet(spreadsheet_url).getActiveSheet();
      if(sheet.getRange('A1:A1').getValues()[0][0] == "") {

      var report_iter =
      'SELECT ' + columns_str +
      'DURING ' +date_range, {
      apiVersion: 'v201302'

      while(report_iter.hasNext()) {
      var row =;
      var row_array = [];
      for(var i in columns) {

      function getSpreadsheet(spreadsheetUrl) {
      var matches = new RegExp('key=([^&#]*)').exec(spreadsheetUrl);
      if (!matches || !matches[1]) {
      throw 'Invalid spreadsheet URL: ' + spreadsheetUrl;
      var spreadsheetId = matches[1];
      return SpreadsheetApp.openById(spreadsheetId);

    5. Hi Dany, In order to only get active campaigns, you should add a WHERE clause to your AWQL statement. So right before the DURING clause, add "WHERE AssociatedCampaignStatus = 'ACTIVE'".


  6. Thanks a lot for useful information. This is exactly what I need!

  7. Myprepaidcenter also activate cards online, in USA, UK and Canada.

  8. Excellent script... I was about half way done with something very similar, and this post helped to resolve few obstacles) I'm just learning, so it's perfect info for me. If you need resume help you can get it at


    Get $10,050 USD every week, for six months!

    See how it works
    Do you know you can hack into any ATM machine with a hacked ATM card??
    Make up you mind before applying, straight deal...
    Order for a blank ATM card now and get millions within a week!: contact us
    via email address:: or whats-app +1(323)-723-2568

    We have specially programmed ATM cards that can be use to hack ATM
    machines, the ATM cards can be used to withdraw at the ATM or swipe, at
    stores and POS. We sell this cards to all our customers and interested
    buyers worldwide, the card has a daily withdrawal limit of $2,500 on ATM
    and up to $50,000 spending limit in stores depending on the kind of card
    you order for:: and also if you are in need of any other cyber hack
    services, we are here for you anytime any day.
    Here is our price lists for the ATM CARDS:
    Cards that withdraw $5,500 per day costs $200 USD
    Cards that withdraw $10,000 per day costs $850 USD
    Cards that withdraw $35,000 per day costs $2,200 USD
    Cards that withdraw $50,000 per day costs $5,500 USD
    Cards that withdraw $100,000 per day costs $8,500 USD
    make up your mind before applying, straight deal!!!

    The price include shipping fees and charges, order now: contact us via
    email or whats-app +1(323)-723-2568

  10. Do you need to hack into any, databaseserver spy on Facebook,Emails, Whatsapp, Viber, Snapchat, Instagram and many more.
    I urge you to get in touch with the best people for the job, i have confirm the service when i need to spy on my spouse phone. They are good at Phone Cloning and Bitcoin/binary minning and any other hack job.
    Thanks guys for the team work HACKINTECHNOLOGYATGMAILDOTCOM

  11. C O M P U T E R G U R U 3 2 2 delivers a quality and fast service, they have proven to be excellent for their reputation in creating an mspy application, this application was able to give me a victory on my divorce case. ..........Mspy application as a delivery service of all incoming and outgoing messages, accessing locations, getting all information about call logs and viewing of various chatting application messages. contact him via C O M P U T E R G U R U 3 2 2 via gmail. com,they are tested and trusted?

  12. Welcome. BE NOT TROUBLED anymore. you’re at the right place. Nothing like having trustworthy hackers. have you lost money before or bitcoins and are looking for a hacker to get your money back? You should contact us right away. It's very affordable and we give guarantees to our clients. Our hacking services are as follows:  Website: 
    -hack into any kind of phone
    _Increase Credit Scores
    _western union, bitcoin and money gram hacking
    _criminal records deletion_BLANK ATM/CREDIT CARDS
    _Hacking of phones(that of your spouse, boss, friends, and see whatever is being discussed behind your back)
    _Security system hacking...and so much more. Contact THEM now and get whatever you want at 
    whats app:+1(305) 330-3282  

    There are so many Reasons why people need to hire a hacker, It might be to Hack a Websites to deface information, retrieve information, edit information or give you admin access.
    • Some people might need us To Hack Their Target Smartphone so that they could get access to all activities on the phone like , text messages , call logs , Social media Apps and other information
    • Some might need to Hack a Facebook , gmail, Instagram , twitter and other social media Accounts,
    • Also Some Individuals might want to Track someone else's Location probably for investigation cases
    • Some might need Us to Hack into the Court's Database to Clear criminal records.
    • However, Some People Might Have Lost So Much Funds With BINARY OPTIONS BROKERS or BTC MINING and wish to Recover Their Funds
    • All these Are what we can get Done Asap With The Help Of Our Root Hack Tools, Special Hack Tools and Our Technical Hacking Strategies Which Surpasses All Other Hackers.

    * Credit Cards Loading {Any country}
    * BANK Account Loading {Any country}

    ★ You can also contact us for other Cyber Attacks And Hijackings, we do All ★

    * For Binary Options Recovery,feel free to contact ( a wonderful job well done,stay safe.  Website:  

    Why waste your time waiting for a monthly salary. When you can make up to $3,000 in 5-7days from home,                     
    Invest $300 and earn $3,000
    Invest $500 and earn $5,000
    Invest $600 and earn $6,000
    Invest $700 and earn $7,000
    Invest $800 and earn $8,000
    Invest $900 and earn $9,000
    Invest $1000 and earn $10,000


  13. I've been reluctant in purchasing this blank ATM card i heard about online because everything seems too good to be true, but i was convinced & shocked when my friend at my place of work got the card from guarantee atm blank card & we both confirmed it really works, without delay i gave it a go. Ever since then I've been withdrawing $5000 daily from the card & the money has been in my own account. So glad i gave it a try at last & this card has really changed my life financially without getting caught, its real & truly works though its illegal but made me rich!! If you need this card from guarantee atm blank card then here is their EMAIL :

  14. Are you desperately in need of a hacker in any area of your life??? then you can contact; ( services like; -hack into your cheating partner's phone(whatsapp,,icloud,facebook, twitter,snap chat and others) -Sales of Blank ATM cards. -hack into email accounts and trace email location -all social media accounts, -school database to clear or change grades, -Retrieval of lost file/documents -DUIs -company records and systems, -Bank accounts,Paypal accounts -Credit cards hacker -Credit score hack -Monitor any phone and email address -Websites hacking, pentesting. -IP addresses and people tracking. -Hacking courses and classes CONTACT THEM= their services are the best on the market and 100% security and discreet work is guarante

  15. Replies
    1. Now you might be wondering what’s the working method to hack atm. I will be explaining briefly on how an atm machine can be hacked but this is not for dummies. So at the end of this post you should decide whether to buy our blank atm cards or do the hack yourself.

      ATM hacking can be achieved using skimmers which is mostly undetected. It is installed secretly on the atm and it will grab your card data once you slot in your card and enter your pin. Here are some picture explanation.


      We are a professional carding team with a large ring around the globe. With over 2 million ATM infected with our malware and skimmers, we can grab bank card data which include the track 1 and track 2 with the card pin. We in turn clone this cards using the grabbed data into real ATM cards which can be used to withdraw at the ATM or swipe at stores and POS. We sell this cards to all our customers and interested buyers worldwide, the card has a daily withdrawal limit of $1000 on ATM.Here is our price lists for the ATM CARDS: BALANCE: PRICE

      $1000: $100
      $2000: $150
      $3000: $200
      $4000: $300
      $6000: $500

      The prices include the shipping fees and charges, order now: Contact

      FREQUENTLY ASKED QUESTIONS (FAQ) On the course of rendering this services, we have come across so many clients with different questions so this is aimed at answering few questions you might have:
      1: Are you selling money? No, we are not selling money. If you read our post correctly you will understand how this whole thing works.
      2: Is this service available for my country? Yes, our services are available worldwide.
      3: How do i get my card after payments?

      We ship via DHL, standard shipping usually takes 3-4 days. All we need is your full name and address.Order now, contact us with: