Quick guide to ad group budget distribution with Google Ads script

Created by Todd Belcher, Modified on Fri, 17 Jan at 1:27 AM by Todd Belcher

There are campaign management tools that will allocate budget for you throughout the day, and then there is Google Ads Script


This is an example script that we have used to ensure campaign budgets are spread across ad groups throughout the day in such a way that they will all get better distributed exposure.




function main() {
  // Set to the number of time slots per day to use as a basis for balancing ad groups
  const TIME_SLOTS = 8;
  const TIME_SLOT_DURATION = 24 / TIME_SLOTS; // Hours per slot

  // Define a minimum bid to prevent excessively low values
  const MIN_BID = 0.5; // Adjust based on your market

  // Get current time and determine the active time slot
  const now = new Date();
  const currentHour = now.getHours();
  const currentSlot = Math.floor(currentHour / TIME_SLOT_DURATION); // 0-indexed slot

  Logger.log(`Current time slot: ${currentSlot + 1}/${TIME_SLOTS}`);

  // Fetch all enabled campaigns
  const campaigns = AdsApp.campaigns()
    .withCondition("Status = ENABLED")
    .get();

  while (campaigns.hasNext()) {
    const campaign = campaigns.next();

    // Get the campaign's daily budget dynamically
    const campaignBudget = campaign.getBudget().getAmount();
    const timeSlotBudget = campaignBudget / TIME_SLOTS; // Allocate evenly across time slots

    Logger.log(
      `Campaign "${campaign.getName()}" has a daily budget of ${campaignBudget}. Allocating ${timeSlotBudget} for this time slot.`
    );

    // Fetch all enabled ad groups within the campaign
    const adGroups = campaign.adGroups()
      .withCondition("Status = ENABLED")
      .get();

    const activeAdGroupsCount = adGroups.totalNumEntities();
    if (activeAdGroupsCount === 0) continue;

    // Calculate the budget per ad group for the current time slot
    const adGroupBudget = timeSlotBudget / activeAdGroupsCount;

    while (adGroups.hasNext()) {
      const adGroup = adGroups.next();

      // Fetch today's spend for the ad group
      const stats = adGroup.getStatsFor("TODAY");
      const spendSoFar = stats.getCost();

      // Calculate remaining budget for the ad group in this slot
      const remainingBudget = adGroupBudget - spendSoFar;

      // Fetch historical performance data for the ad group
      const historicalStats = adGroup.getStatsFor("LAST_7_DAYS");
      const averageCpc = historicalStats.getAverageCpc();

      // Fetch current CPC bid
      const currentCpc = adGroup.bidding().getCpc();

      // Determine a new bid based on performance and remaining budget
      let newBid = currentCpc;
      if (remainingBudget > 0) {
        // Increase bid to encourage more spend, considering historical CPC and a minimum bid
        newBid = Math.max(currentCpc * 1.2, averageCpc, MIN_BID);
      } else {
        // Decrease bid to slow down spend
        newBid = Math.max(currentCpc * 0.8, MIN_BID);
      }

      // Update the ad group CPC bid
      adGroup.bidding().setCpc(newBid);

      Logger.log(
        `Adjusted bid for ad group "${adGroup.getName()}" to ${newBid}.`
      );
    }
  }
}


Watch out! This is going to modify all active campaigns. Preview and run in a safe area if you are unsure of what will happen. The script can be modified to pick up only some campaigns, of course, for example:

function main() {
  const remarketingCampaigns = AdsApp.campaigns()
    .withCondition("Name CONTAINS 'Remarketing'")
    .withCondition("Status = ENABLED") // Optional: Fetch only active campaigns
    .get();

  while (remarketingCampaigns.hasNext()) {
    const campaign = remarketingCampaigns.next();
    Logger.log(`Remarketing Campaign: ${campaign.getName()}`);
  }
}


This would check through all active campaigns for one that has a name containing 'Remarketing'


Was this article helpful?

That’s Great!

Thank you for your feedback

Sorry! We couldn't be helpful

Thank you for your feedback

Let us know how can we improve this article!

Select at least one of the reasons
CAPTCHA verification is required.

Feedback sent

We appreciate your effort and will try to fix the article