The Scripts Desk
Budget Pacing Guardian: know by 9am if the month is drifting
A read-only Google Ads Script that compares month-to-date spend with where the month should be, projects the landing figure, and emails you when the pace drifts.
Overspending is discovered on the 28th; underspending is discovered in the monthly report. Both are pacing problems, and both are visible by breakfast if something simply checks the arithmetic every morning. That is all this script does.
It reads month-to-date cost, works out how far through the month you are in the account's own timezone, projects where spend will land at the current rate, and logs the verdict. If you give it an email address and the pace has drifted beyond your threshold, it tells you. It changes nothing in the account: no budgets are touched, no campaigns are paused. It reports.
Before you start
- You need access to the Google Ads account and permission to authorise scripts.
- Decide your monthly spend target for the account and your tolerance for drift. Ten percentage points is a sensible starting threshold.
- The email alert is optional. With no address set, the script only writes to the script log.
The script
/**
* Budget Pacing Guardian
* The SEM Dispatch Vault, semdispatch.com/vault
*
* Compares month-to-date spend with the elapsed share of the month,
* projects the landing figure, and optionally emails on drift.
* Read-only: this script never changes anything in the account.
*/
var CONFIG = {
// Your monthly spend target for this account, in the account currency.
MONTHLY_BUDGET: 5000,
// Alert when pace drifts more than this many points from the calendar.
// 0.10 means: flag when spend share is 10+ points ahead of or behind
// the share of the month that has elapsed.
ALERT_THRESHOLD: 0.10,
// Optional. An email address for drift alerts, e.g. "you@example.com".
// Leave empty to log only.
EMAIL: ""
};
function main() {
var account = AdsApp.currentAccount();
var tz = account.getTimeZone();
var currency = account.getCurrencyCode();
var spend = account.getStatsFor("THIS_MONTH").getCost();
var now = new Date();
var day = parseInt(Utilities.formatDate(now, tz, "d"), 10);
var year = parseInt(Utilities.formatDate(now, tz, "yyyy"), 10);
var month = parseInt(Utilities.formatDate(now, tz, "M"), 10);
var daysInMonth = new Date(year, month, 0).getDate();
var monthElapsed = day / daysInMonth;
var spendShare = CONFIG.MONTHLY_BUDGET > 0 ? spend / CONFIG.MONTHLY_BUDGET : 0;
var drift = spendShare - monthElapsed;
var projected = monthElapsed > 0 ? spend / monthElapsed : 0;
var lines = [
"Budget Pacing Guardian, " + account.getName(),
"Day " + day + " of " + daysInMonth +
" (" + (monthElapsed * 100).toFixed(0) + "% of the month elapsed)",
"Spend so far: " + currency + " " + spend.toFixed(2) +
" of " + currency + " " + CONFIG.MONTHLY_BUDGET.toFixed(2) +
" (" + (spendShare * 100).toFixed(0) + "% of target)",
"Projected landing at this rate: " + currency + " " + projected.toFixed(2),
"Drift vs calendar: " + (drift >= 0 ? "+" : "") + (drift * 100).toFixed(1) + " points"
];
var verdict;
if (Math.abs(drift) <= CONFIG.ALERT_THRESHOLD) {
verdict = "ON PACE within threshold.";
} else if (drift > 0) {
verdict = "AHEAD of pace: at this rate the account overspends the target.";
} else {
verdict = "BEHIND pace: at this rate the account underspends the target.";
}
lines.push(verdict);
var report = lines.join("\n");
Logger.log(report);
var shouldAlert =
CONFIG.EMAIL !== "" && Math.abs(drift) > CONFIG.ALERT_THRESHOLD;
if (shouldAlert) {
MailApp.sendEmail(
CONFIG.EMAIL,
"[Pacing] " + account.getName() + ": " +
(drift > 0 ? "ahead of" : "behind") + " budget pace",
report
);
}
}
Set it up
- In Google Ads, open Tools, then Bulk actions, then Scripts.
- Create a new script, delete the placeholder, and paste the code above.
- Set
MONTHLY_BUDGETto your target, andEMAILif you want the alert. - Authorise the script when prompted, then use Preview to check the log output.
- Schedule it daily, early morning in the account timezone. The point is to read it before the day's decisions, not after them.
How to read the report
Drift is the gap between the share of budget spent and the share of the month elapsed. Plus four points on day 20 is usually noise; plus fourteen points on day 8 is a decision. The projected landing figure assumes the current average daily rate holds, which it rarely exactly does, so treat it as a heading, not a forecast. When the drift alert fires two mornings in a row, that is the signal to look at what changed: search demand, a broad match expansion, a new campaign out of learning, or a budget that no longer matches the plan.
One honest limitation: THIS_MONTH reporting can lag by a few hours, so the morning run reads slightly behind live delivery. For pacing decisions that is fine; for intraday spike detection you want an hourly anomaly script instead, which is a different tool on this shelf.