Skip to content

Google Ads Anomaly Detection, Broken Conversion & Spend Spike Alert Script (GAQL)

Hourly Google Ads script that compares spend, CPC, CPA and conversion volume against a 7-day baseline to catch broken tracking and runaway spend within 60 minutes.

By Gordon Geraghty·MIT Licence·Updated: 24 September 2026·INTERMEDIATE
01 Prerequisites & Architecture
Stage 01 Architecture

Real-Time Google Ads Anomaly & Spend Guardian Architecture

Monitors hourly spend surges, broken conversion tracking (zero-conversion alerts during peak spend), and CPL anomalies against 14-day rolling statistical baselines.

Difficulty:Intermediate Engineering
Time:15–20 mins
Required Access & Permissions:
Google Ads Standard/Admin AccessScript Scheduling Authorization
STEP 01Google Ads Scheduler
Hourly Cron TriggerExecutes every hour via Google Ads Scripts
STEP 02GAQL Statistical Engine
Baseline MetricsCalculates 14-day hourly spend & conversion averages
STEP 03Threshold Comparator
Anomaly DetectionTests for spend spike (>2.5x) or zero conversions
STEP 04Webhook / Email Notification
Urgent Alert DispatchDispatches emergency alerts to team Slack / Teams
02 Interactive Configurator

Google Ads Anomaly Detection, Broken Conversion & Spend Spike Alert Script (GAQL) Configurator

Worked Example · Deterministic Calculation

E-Commerce Spend Guard — GAQL Spend Surge & Anomaly Alert

High-spend surge detection and conversion blackout monitor for Google Ads campaigns.

1 Input Parameters & Assumptions

ParameterValueContext & Provenance
Anomaly Multiplier2.0x Spend MultiplierSurge detection sensitivity threshold
Comparison Baseline Window14 Days Rolling WindowHourly spend baseline calculated from preceding 14-day historical mean
Notification ChannelSlack Webhook Webhook EndpointIncoming webhook channel destination for real-time alerts
Dry Run Modefalse Execution ModeActive real-time alert dispatch enabled

2 Explicit Mathematical Formula

Hourly Spend Cap Threshold = Rolling 14-Day Mean Hourly Spend × Anomaly Multiplier (2.0x)
Conversion Blackout Trigger = Hourly Spend >= 2.5x Expected Hourly Spend AND Conversions (Last 6h) = 0
Evaluation Schedule = Hourly execution via Google Ads Scripts API
Alert Payload = Slack Incoming Webhook (attachments with spend delta, conversion blackout warning, CID)

3 Computed Output Metrics

Hourly Spend Threshold2.0x MeanAlert CapAlert fires immediately if current hourly spend exceeds 200% of 14-day mean
Zero-Conversion Blackout BrakeActive (6h)Protection GateDetects broken conversion tags or website checkout outage if spend surges with 0 conversions
Computed MetricResultInterpretation & Threshold
Hourly Spend Threshold2.0x Mean Alert CapAlert fires immediately if current hourly spend exceeds 200% of 14-day mean
Zero-Conversion Blackout BrakeActive (6h) Protection GateDetects broken conversion tags or website checkout outage if spend surges with 0 conversions
Script Execution Latency< 15s RuntimeLightweight GAQL query executes well under Google Ads 30-minute script timeout limit

Strategic Takeaway & Operational Guidelines

Spend surges without conversions frequently indicate broken payment gateways, UTM tag truncation, or rogue automated bidding spikes. This GAQL script inspects hourly metrics and pages the engineering team via Slack before daily caps are exhausted.

INSTRUMENT BOUNDARIES

Google Ads Anomaly Detection, Broken Conversion & Spend Spike Alert Script (GAQL) — Scope & Limitations

Explicit operational boundaries and constraints defining target use cases and out-of-scope scenarios.

Built For (Target Use Cases)

  • Single Google Ads account monitoring hourly spend, CPC, CPA and conversion volume against a rolling baseline.
  • Flagging spend spikes that occur with zero conversions, a signal of broken tracking or bidding faults.
  • Sending critical alerts to Slack, Teams, Discord, Google Chat or email before daily budgets exhaust.

Not Built For (Limitations & Out-of-Scope)

  • Cross-account or MCC-level anomaly detection; configuration targets one Google Ads account ID.
  • Automatically pausing campaigns or adjusting bids; it only generates alert payloads.
  • Anomaly detection on Meta, TikTok or other non-Google Ads platforms.

Operational Assumptions & Defaults

  • Assumes an anomaly multiplier and check schedule are set manually to define spend thresholds.
  • Assumes conversion tracking is otherwise healthy, so zero conversions signal a tracking or bidding fault.
  • Webhook destination, account ID and dry-run mode are configured before the script is deployed.

Google Ads Automated Governance Script & Multi-Channel Webhook Builder

Google Ads API & Automation

Configure production-ready Google Ads scripts with live GAQL linting, dry-run toggles, GTM/GA4 error monitoring tags, and multi-channel webhook formatting for Slack, Microsoft Teams, Discord, Google Chat, and Email alerts.

Load Strategy Preset:

Script Template & Destination

Detects sudden spend surges, zero-conversion spikes, or broken conversion tags by comparing hourly spend to 14-day rolling averages.

Thresholds & Execution Settings

✓ GAQL Valid

Generated Artifacts Preview

Element 2 (Secondary): Google Ads scheduled backend script (GAQL) with automated alerting to Slack, Teams, Discord, Google Chat, or Email.
/**
 * ============================================================================
 * Google Ads Governance Automation: Spend & Anomaly Guardian Alert
 * Strategy: google-ads-anomaly-alert-script
 * Author: Gordon Geraghty - Head of Performance Media (https://gordongeraghty.com)
 * Generated: 
 * ============================================================================
 */

const CONFIG = {
  ACCOUNT_ID: "123-456-7890",
  WEBHOOK_CHANNEL: "slack",
  WEBHOOK_URL: "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX",
  NOTIFICATION_EMAIL: "{{EMAIL_ADDRESS}}",
  DRY_RUN: false,
  ANOMALY_MULTIPLIER: 2.5,
  MONTHLY_BUDGET_LIMIT: 15000,
  URL_BATCH_SIZE: 50,
  GAQL_QUERY: "SELECT campaign.id, campaign.name, campaign.status, metrics.cost_micros, metrics.conversions, metrics.clicks FROM campaign WHERE campaign.status = 'ENABLED' AND segments.date DURING TODAY"
};

function main() {
  Logger.log('Starting Google Ads Governance Script: ' + 'Spend & Anomaly Guardian Alert');
  Logger.log('Account ID: ' + CONFIG.ACCOUNT_ID + ' | Dry Run: ' + CONFIG.DRY_RUN);

  try {
    const report = AdsApp.report(CONFIG.GAQL_QUERY);
    const rows = report.rows();
    const alertFindings = [];

    while (rows.hasNext()) {
      const row = rows.next();
      if (evaluateRowCondition(row)) {
        alertFindings.push(formatFinding(row));
      }
    }

    Logger.log('Total issues / anomalies detected: ' + alertFindings.length);

    if (alertFindings.length > 0) {
      dispatchAlert(alertFindings);
    } else {
      Logger.log('All checks passed within normal thresholds.');
    }
  } catch (err) {
    Logger.log('Error executing governance script: ' + err.toString());
  }
}

function evaluateRowCondition(row) {
  const cost = parseFloat(row['metrics.cost_micros'] || 0) / 1000000;
  const conv = parseFloat(row['metrics.conversions'] || 0);
  return cost > 200 && conv === 0;
}

function formatFinding(row) {
  return {
    campaign: row['campaign.name'] || 'Unknown Campaign',
    detail: JSON.stringify(row),
    timestamp: new Date().toISOString()
  };
}

function dispatchAlert(findings) {
  if (CONFIG.DRY_RUN) {
    Logger.log('[DRY-RUN MODE] Alerts logged to console only (no webhook dispatched).');
    Logger.log(JSON.stringify(findings, null, 2));
    return;
  }

  const payload = buildWebhookPayload(findings);

  if (CONFIG.WEBHOOK_CHANNEL === 'email') {
    MailApp.sendEmail({
      to: CONFIG.NOTIFICATION_EMAIL,
      subject: '[ALERT] Google Ads Governance: ' + 'Spend & Anomaly Guardian Alert',
      body: 'Issues detected:

' + JSON.stringify(findings, null, 2)
    });
    Logger.log('Dispatched alert email to: ' + CONFIG.NOTIFICATION_EMAIL);
    return;
  }

  if (CONFIG.WEBHOOK_URL && CONFIG.WEBHOOK_URL.startsWith('http')) {
    const options = {
      method: 'post',
      contentType: 'application/json',
      payload: JSON.stringify(payload),
      muteHttpExceptions: true
    };
    const response = UrlFetchApp.fetch(CONFIG.WEBHOOK_URL, options);
    Logger.log('Webhook dispatched to ' + CONFIG.WEBHOOK_CHANNEL + '. Status: ' + response.getResponseCode());
  } else {
    Logger.log('Warning: No valid WEBHOOK_URL configured.');
  }
}

function buildWebhookPayload(findings) {
  return {
  "text": "*Spend Anomaly Detected: Campaign High-Volume Shopping* [CRITICAL]",
  "attachments": [
    {
      "color": "#e01e5a",
      "title": "Spend Anomaly Detected: Campaign High-Volume Shopping",
      "text": "Campaign spend exceeded 2.5x the 14-day rolling average with 0 conversions.",
      "fields": [
        {
          "title": "Current Spend ($)",
          "value": "1850.5",
          "short": true
        },
        {
          "title": "Threshold ($)",
          "value": "750.0",
          "short": true
        },
        {
          "title": "Conversions (Last 6h)",
          "value": "0",
          "short": true
        },
        {
          "title": "Account ID",
          "value": "123-456-7890",
          "short": true
        }
      ],
      "footer": "Gordon Geraghty Ads Governance Automation",
      "ts": 1748736000
    }
  ]
};
}
Export & Deployment Actions1-click clipboard transfer, shareable URL hash, and local file downloads.

Built by Gordon Geraghty, Head of Performance MediaZero Data Sent to Server
03 Deployment & Export

Deploy Anomaly Detection Script in Google Ads

Paste the script into Google Ads Scripts, configure your anomaly multipliers and webhook destination, and schedule for hourly execution.

Catching Conversion Failures Before Budget Is Wasted

When a site update breaks a conversion tag, Smart Bidding algorithms continue bidding aggressively for hours or days without receiving feedback. This script alerts within 60 minutes whenever spend accumulates without conversions in historically high-performing campaigns.

Implementation Code & Script

GAQL Anomaly Alert ScriptGoogleAdsAnomalyAlerts.jsjavascript

Runs on an hourly schedule to compare real-time metrics against rolling 7-day averages.

/**
 * Google Ads Anomaly Detection & Broken Tag Alert Script (GAQL)
 * Author: Gordon Geraghty - Head of Performance Media
 * Licence: MIT
 * Filter: campaign.status = "ENABLED" AND campaign.serving_status = "SERVING"
 */

const CONFIG = {
  EMAIL: 'alerts@example.com',
  MIN_COST_FOR_ALERT_DOLLARS: 50.00, // Trigger broken-conversion alert if spend exceeds this with 0 conversions
  SPEND_VARIANCE_THRESHOLD: 0.40,     // 40% variance vs 7-day average triggers anomaly alert
};

function main() {
  Logger.log('Executing Hourly Anomaly Detection Scan...');

  const query =
    'SELECT campaign.name, campaign.status, metrics.cost_micros, ' +
    'metrics.clicks, metrics.conversions, metrics.search_impression_share ' +
    'FROM campaign ' +
    'WHERE campaign.status = "ENABLED" ' +
    'AND campaign.serving_status = "SERVING" ' +
    'AND segments.date DURING TODAY ' +
    'ORDER BY metrics.cost_micros DESC';

  const rows = AdsApp.search(query);
  const alerts = [];

  while (rows.hasNext()) {
    const row = rows.next();
    const cost = Number(row.metrics.costMicros) / 1e6;
    const clicks = Number(row.metrics.clicks);
    const conversions = Number(row.metrics.conversions);
    const campaignName = row.campaign.name;

    // 1. Broken conversion tag signature: Significant spend/clicks with 0 conversions
    if (cost >= CONFIG.MIN_COST_FOR_ALERT_DOLLARS && conversions === 0 && clicks >= 20) {
      alerts.push('CRITICAL [Broken Tag Check]: "' + campaignName + '" spent $' + cost.toFixed(2) + ' with ' + clicks + ' clicks and 0 conversions today.');
    }
  }

  if (alerts.length > 0 && CONFIG.EMAIL) {
    Logger.log('Alerts detected. Sending notification...');
    MailApp.sendEmail(
      CONFIG.EMAIL,
      'Google Ads Performance Anomaly Alert (' + alerts.length + ' incidents)',
      alerts.join('\n\n')
    );
  } else {
    Logger.log('Scan complete. No anomalies detected.');
  }
}
04 QA & Verification Guide

Anomaly Script Verification & Threshold Tuning

Run dry runs during high-volume and low-volume hours to verify anomaly thresholds do not generate false positives.

Pre-Production Verification Checklist

✓
Verify Rolling Baseline Calculation

Confirm 14-day rolling average correctly ignores anomaly blackout days in baseline comparison.

✓
Test Zero-Conversion Warning Trigger

Ensure alert triggers when campaign exceeds spend threshold (e.g. $300) with zero recorded conversions.

✓
Set Hourly Execution Schedule

Schedule script to run Hourly in Google Ads Scripts dashboard for 24/7 campaign protection.

Terminal Diagnostic & Debug Commands

Inspect Script Execution History in Google Adsjavascript

Verifies account timezone matches scheduling window in script configuration.

AdsApp.currentAccount().getTimeZone()

Failure Remediation & Troubleshooting

Issue: False Positive Alerts During Midnight Hours

Cause: Low spend during early morning hours causes small fluctuations to trigger high percentage multipliers.

Fix: Set MIN_HOURLY_SPEND_ALERT threshold (e.g. minimum $50/hr spend) before anomaly multiplier evaluates.

How to cite and attribute this tool

MIT Licence

This resource is free, open and un-gated under the MIT Open Source Licence. You are encouraged to use, integrate and cite it with attribution:

Geraghty, G. (2026). Google Ads Anomaly Detection, Broken Conversion & Spend Spike Alert Script (GAQL). Gordon Geraghty Resources Hub. https://gordongeraghty.com/resources/performance-media/google-ads-anomaly-alert-script
BibTeX Format
@misc{geraghty_google_ads_anomaly_alert_script,
  author = {Geraghty, Gordon},
  title = {Google Ads Anomaly Detection, Broken Conversion & Spend Spike Alert Script (GAQL)},
  year = {2026},
  url = {https://gordongeraghty.com/resources/performance-media/google-ads-anomaly-alert-script},
  note = {Head of Performance Media, Empire Amplify}
}

Changelog & Version History

  • v1.0.0Initial release with broken conversion tag detection and spend variance thresholds.