ads-system.store

Budget Pacing in Google Ads Scripts

Pace spend against the month, not the daily budget, because Google is allowed to overspend on any single day. Read through GAQL rather than AdsApp.campaigns() so Performance Max is included, and alert on band crossings rather than every morning.

A monthly budget overspends for one of two reasons: nobody looked until the 28th, or somebody looked at the wrong number. Pacing arithmetic is trivial. Getting it to run every morning against the whole account, including the campaign types the obvious script misses, is the part worth building.

The arithmetic

Two numbers, one comparison.

expected = monthly_budget * (days_elapsed / days_in_month)
pace     = spend_to_date / expected

A pace of 1.0 is on track, 1.2 means twenty per cent ahead, 0.8 means twenty per cent behind. Both directions are problems: overspend costs money, underspend loses volume that cannot be recovered once the month closes.

Two refinements make the number honest. Count elapsed days as completed days, since today is still spending. And if the account has a strong weekly pattern, weight the expected line by weekday rather than treating every day as equal — an account that does most of its business on weekdays looks alarmingly behind every Monday morning under a flat model.

Where the daily budget number is wrong

The daily budget in the interface is not a cap on a day’s spend. Google can spend more on a high-traffic day and compensate across the month, with the monthly limit at roughly 30.4 times the daily budget. Any script that alerts because yesterday exceeded the daily budget will fire constantly and be ignored within a fortnight.

Pace against the month. Look at a single day only for anomalies, which is a different question with a different baseline.

Shared budgets and where the total lives

If campaigns sit on a shared budget, per-campaign pacing is misleading: the campaigns compete for one pool, and one of them running hot is not necessarily a problem. Pace the budget, not the campaign, when a shared budget is in use.

Note also that the client’s real monthly number usually lives outside Google Ads, in a spreadsheet or an email. A pacing script needs that figure as an input. Keeping it in a Sheet the script reads is more durable than hard-coding it, because someone will change it and that someone will not be you.

Covering the whole account

This is where most pacing scripts fail. AdsApp.campaigns() returns Search and Display only, so a pacing check built on it omits Performance Max entirely — often the largest spender. Read through GAQL instead:

SELECT campaign.name,
       campaign.advertising_channel_type,
       metrics.cost_micros
FROM campaign
WHERE segments.date BETWEEN '2026-09-01' AND '2026-09-04'
  AND campaign.status != 'REMOVED'

Then verify: the total the script produces should match the account total in the interface for the same window, to the currency unit. If it does not, something is being excluded. The Performance Max guide explains why.

Alerting without being ignored

  • Do not alert daily. Alert when pace crosses a band, typically outside 0.85 to 1.15, and only once per crossing.
  • Suppress the first days of the month. On the 2nd, one strong day puts pace at 2.0. Start checking around day five.
  • Give the projection, not just the ratio. “On track for 1,340 against a 1,100 budget” is actionable. “Pace 1.22” requires the reader to do arithmetic before they can care.
  • Include the fix. The daily budget change that would bring it back to 1.0 for the rest of the month is one line of division and it turns the alert into an instruction.

Should the script change the budget itself?

Usually not, for two reasons. Budget changes interact with bid strategy learning, so a script that adjusts budgets daily creates instability it then reports on. And a wrong budget change compounds silently for the rest of the month.

If you do automate it, cap the size of any single change, never let it move more than once a day, and log every change with the before and after value so it can be reversed by hand. That pattern is covered in the safe automation checklist.

The read-only version is the budget pacing monitor.

Questions

Why did my campaign spend more than its daily budget?
Daily budget is an average, not a cap. Google can spend more on higher traffic days and compensate across the month, so alerting on single day overspend produces constant false alarms.
How do I calculate pacing?
Expected spend is the monthly budget times the fraction of the month elapsed. Pace is spend to date divided by expected spend. Anything outside roughly 0.85 to 1.15 is worth an alert.
Does a pacing script see Performance Max spend?
Only if it reads through GAQL. A script using AdsApp.campaigns() covers Search and Display, so it will understate total spend in most modern accounts.
Should a script adjust budgets automatically?
Rarely. Budget changes disturb bid strategy learning, and an incorrect change compounds for the rest of the month. If automated, cap the change size and log every adjustment.