Case Studies / Ad Group Budget Script
Legal Google Ads Scripts Google Sheets Automation

Google Ads Doesn't Have Ad Group Budgets. So We Built One.

A law firm client wanted to cap spend per ad group — rotating budget toward whichever practice areas still had capacity, and away from the ones already full. Google Ads doesn't work that way. The client wouldn't restructure his campaigns. So we built a Google Ads Script that reads a Google Sheet every hour, pauses any ad group that's hit its monthly limit, and resets the whole thing automatically on the first of the month.

Hourly

Script runs 24/7 checking spend vs. budget

1 Sheet

Client sets and adjusts all budgets himself

Day 1

Everything reactivates automatically each month

The Problem

A Law Firm With a Specific Budget Problem — and Campaigns He Refused to Touch

The client was a solicitor running Google Ads across several practice areas — personal injury, family law, conveyancing, employment disputes. Each of these areas had its own ad groups, and each generated a different type of case: different average payout, different case duration, different workload on his team.

The problem was capacity. When his pipeline was full of personal injury cases — which tend to run long and tie up fee earners for months — he didn't want to keep acquiring more of them. He wanted to shift that budget over to conveyancing or family law, which moved faster and freed up capacity sooner. Not permanently, just until the pipeline balanced out.

The correct way to handle this in Google Ads is to run each practice area in its own campaign with its own daily budget, and adjust those daily budgets as the caseload shifts. That would give him direct, clean control. The client knew this. We explained it clearly. He didn't want to do it. His existing campaigns had years of performance history — Quality Scores, auction learnings, impression share data — and he wasn't prepared to risk resetting any of that by restructuring.

So the campaigns stayed as they were: multiple practice area ad groups sitting inside the same campaigns, sharing campaign-level budgets. What the client wanted was the ability to set a monthly spend cap on each individual ad group — something Google Ads simply doesn't offer as a native feature.

"He knew the right answer. He didn't want the right answer. He wanted to control spend by ad group without losing years of campaign history — and he wanted to do it from a spreadsheet he could edit himself whenever his caseload shifted."

The Constraint

Ad Group Budgets Don't Exist in Google Ads. The Only True Spend Cap Is a Pause.

Google Ads budgets live at the campaign level. There's no built-in way to say "this ad group can spend £500 this month and then stop." You can add bid adjustments to an ad group — lowering bids will slow spend — but it won't cap it. If the auction is competitive and the budget headroom at campaign level is available, a low-bid ad group can still overspend a target you have in your head.

The only mechanism that genuinely stops an ad group from spending any more money is pausing it. So that's the model we built around: the script checks spend each hour, and when an ad group crosses its monthly budget as set in the spreadsheet, it gets paused. Not bid-adjusted. Paused.

The challenge that introduces is state management. The script needed to know, at month-end, which ad groups were paused by the budget logic versus which ones were already paused before the script ever touched them. Pausing everything at once and unpausing everything at the start of the new month would accidentally reactivate ad groups the client had intentionally switched off for other reasons.

Why we didn't just un-pause everything at month reset

The client had some ad groups that were intentionally paused long-term — old creatives, practice areas he'd wound down, seasonal work he'd suspended. If the reset script un-paused everything, it would reactivate those too. The label approach meant the script only touched ad groups it had paused itself, leaving everything else as it was.

The Architecture

A Google Ads Script Reading a Sheet, Running Hourly, Tracking State With Labels

The whole system is a single Google Ads Script — JavaScript that runs inside the Google Ads environment, with native access to the account's campaign data and the ability to read external data via URL fetch. It connects to a Google Sheet via the Sheets API, runs on an hourly schedule, and uses a Google Ads label as a stateful flag to track which ad groups it has paused itself.

Hourly Script Execution Flow

Step 1 — Read budgets from Google Sheets

Fetches each ad group's Base Budget and Temporary Budget columns — effective budget is the sum of both

Step 2 — Check spend vs. effective budget for each ad group

Queries month-to-date cost and compares it against Base + Temporary budget; also checks if any paused ad group now has new headroom from an added temporary budget

Step 3a — Pause over-budget ad groups + apply label

Any ad group at or above its effective budget is paused; a script-paused label is applied so the reset logic knows to reactivate it later

Step 3b — Auto-unpause if temporary budget creates new headroom

If a script-paused ad group now has spend below its effective budget (client topped it up), the script re-enables it and removes the label — no Google Ads login needed

Step 4 — Write updated spend data back to Google Sheets

Every ad group's current month-to-date cost, remaining budget, and status are written back so the client always sees a fresh view

Step 5 — Monthly reset (first run of day 1 only)

Reactivates all script-paused ad groups, removes the label, and zeroes out all Temporary Budget cells in the sheet — ready for the new month

1

The Spreadsheet the Client Controls

The Google Sheet has one row per ad group and two budget columns the client owns: a Base Budget (his regular monthly allocation for that practice area) and a Temporary Budget (an optional top-up he can add mid-month for a one-off push). The script adds the two together for the effective cap. He can change either figure any time he likes — the next hourly run picks up the new values automatically.

The sheet also shows real-time columns written back by the script: current month-to-date spend, remaining budget, status (Active / Script-Paused), and the timestamp of the last update. Every ad group's position is visible at a glance — spend, headroom, whether the script has paused it. The client can open the sheet at any point and have a complete picture without logging into Google Ads at all.

2

Hourly Spend Check — Month-to-Date Against the Effective Budget

Each time the script runs, it queries Google Ads for the month-to-date cost of every ad group in scope. Month-to-date is the right window because the client's budgets are monthly figures — he thinks in terms of "£800 on personal injury in June", not rolling 30-day windows. The script adds the Base Budget and Temporary Budget columns together, compares that figure against current spend, and flags anything at or above the combined total for pausing.

The same check runs in reverse for paused ad groups: if an ad group is currently paused by the script but the client has since entered a temporary budget that pushes the effective limit above what's been spent, the script treats that as new headroom and reactivates it. He doesn't touch Google Ads — he just edits a cell in the sheet and the ad group is running again within the hour.

3

Pausing With a Label — The State Tracking Trick

When the script pauses an ad group for hitting its budget, it simultaneously applies a Google Ads label — something like script-paused. This label is the only way the script knows, at month-end, which pauses were its own work and which were deliberate client decisions.

Without this, a month-end reset would have to choose between two bad options: un-pause everything (accidentally reactivating ad groups the client had intentionally switched off) or un-pause nothing (requiring the client to manually reactivate everything at the start of each new month, which defeats the point entirely). The label threads that needle cleanly.

4

Automatic Reset on the First of Every Month

On the first run of the first day of a new month, the script detects the date and triggers the reset phase. It queries for all ad groups carrying the script-paused label, sets their status back to Enabled, and removes the label. It also zeroes out every Temporary Budget cell in the sheet — temporary top-ups were for the previous month and don't roll over. Then it immediately runs the normal spend-check flow, so the new month's budget logic is live from the first hour.

The client doesn't need to do anything at month-end. His base budgets carry over as set; temporary budgets clear automatically. If he wants to top up something early in the new month, he just adds a temporary figure to the sheet. Everything else is handled.

In Practice

How the Client Actually Used It Week to Week

In practice, the client opens the sheet once or twice a week. He glances at the Spent and Remaining columns, sees which practice areas are burning through their allocation and which still have room, and decides whether to adjust anything. If personal injury cases are piling up, he drops that ad group's base budget down to what's already been spent and the next hourly run pauses it. If he wants to push harder on conveyancing for the rest of the month, he bumps the base budget up and the script lets it keep running.

The temporary budget column handles one-off situations. If he's had a quiet month and wants to push a particular practice area harder in the final two weeks, he types a figure into the temporary column. The script adds it to the base budget, and if the ad group was already paused, it reactivates it automatically on the next hourly check. No logging into Google Ads. No navigating to the right campaign, the right ad group, the right setting. One cell. Done.

Before this system existed, the client would log into Google Ads directly when he wanted to shift spend around. The problem was that doing budget work inside the Google Ads interface when you're not a paid search specialist is easy to get wrong — wrong campaign, wrong ad group, incorrect pausing, accidentally enabling something that should have stayed off. He was making these kinds of mistakes regularly: changing the wrong ad groups, pausing things he didn't mean to, losing track of what he'd done. The sheet solved this not just by being simpler but by putting everything in one place — spend, status, budget — so the decisions he needed to make were obvious before he made them.

It's worth being clear: this is not the ideal way to structure a Google Ads account. Proper campaign-level budget segmentation is simpler, cleaner, and easier for the algorithm to work with. But the client made a deliberate, informed choice to keep his existing structure, and the script gave him the control he needed within that constraint. Sometimes the job is to build the best solution for the constraints you're actually given.

The Result

Per-Ad-Group Budget Control — Without Touching a Single Campaign.

The client got exactly what he asked for: a way to set monthly spend limits per ad group and adjust them whenever his caseload shifted, without any changes to the underlying campaign structure. The sheet gave him visibility and control. The hourly script gave him near-real-time enforcement. The temporary budget column gave him flexibility for one-off pushes. The label-based reset gave him a clean slate each month with zero manual work.

The bigger win was the reduction in errors. Before, he was going into Google Ads directly and making changes he wasn't always confident about — pausing the wrong ad groups, adjusting the wrong campaigns, losing track of what state things were in. Now all of that context is in one spreadsheet he can read at a glance before he does anything. The decisions are obvious, the execution is one cell, and the script handles the rest.

The same pattern — Google Ads Script reading a Google Sheet for configuration, writing results back for visibility, using labels for state tracking — applies across a wide range of automation needs. Budget caps are one use case; automated dayparting, keyword-level bid adjustments triggered by external signals, and scheduled ad rotation are others.

Hourly

Spend checked 24 times a day — maximum overspend is one hour's worth

1 Cell

Pause, resume, or top up any ad group — script handles the rest within the hour

Zero Errors

No more wrong ad groups paused — all context visible before any decision is made

Need Custom Budget Logic Your Ad Platform Doesn't Support?

If you've hit a wall with what Google Ads or Microsoft Ads can do natively — whether it's budget control, scheduling, automated pausing, or anything else — a Google Ads Script connected to a spreadsheet is often simpler and cheaper than you'd expect. Get in touch and tell us what you're trying to control.

Brendan Andrew Chase

Written by

Brendan Andrew Chase

Google Ads specialist and marketing automation consultant with 10+ years managing paid search for service businesses across the US, UK, and EU. 200+ projects delivered. Founder of Extra Large Marketing Digital, based in Rio de Janeiro.