Google Ads scripts let you automate the boring and the impossible: pausing campaigns at 2am, scaling budgets when demand spikes, and reacting to real-world signals like the weather faster than any human can.

This page is a working library of four free Google Ads weather scripts you can copy, paste, and schedule today. Each one is commented and takes about 10 minutes to set up. No developer required.

The four scripts:

  1. Weather on/off toggler turns campaigns on when it rains (or gets hot) and pauses them when it doesn’t.
  2. Weather budget scaler increases or decreases daily budgets based on conditions instead of hard on/off switching.
  3. Multi-city toggler runs different weather rules for different locations from one script.
  4. Weather performance logger records weather alongside campaign metrics in a Google Sheet so you can prove weather actually moves your numbers.

What Are Google Ads Scripts?

Google Ads scripts are snippets of JavaScript that run inside your Google Ads account. They can adjust bids and budgets, pause or enable campaigns, generate reports, and, most usefully here, fetch external data via APIs and act on it.

That last part is the unlock. A script can call a weather API every hour and change your account based on what comes back. Google’s built-in automated rules can’t do that: they only react to data already inside Google Ads.

Why Weather Matters in Digital Advertising

Weather has a direct, measurable influence on what people buy:

  • Rain increases demand for umbrellas, food delivery, and rideshares.
  • Heatwaves boost sales of air conditioners, cold drinks, and sunscreen.
  • Cold snaps lift demand for heating services, winter fashion, and comfort food.
  • Windy days drive interest in windproof jackets and certain sports gear.

By syncing campaigns with conditions, you only spend when customers are most receptive. For the research behind this, see how weather affects consumer behaviour.

Before You Start: 10-Minute Setup (All Scripts)

Every script in this library uses the same three ingredients:

  1. Campaign labels. Label the campaigns you want controlled, e.g. WT:Rain or WT:Hot. Labels tell the script which campaigns to touch and, just as importantly, which to leave alone.
  2. A weather API key. These scripts use OpenWeatherMap’s One Call API 3.0, which gives hourly forecasts with rain probability. It includes 1,000 free calls a day, but you need to subscribe to the “One Call by Call” plan with a card on file. An hourly script for one city uses 24 calls a day. Tomorrow.io is an alternative. Both work with UrlFetchApp from inside Google Ads scripts.
  3. A schedule. In Google Ads, open Tools, then Bulk actions, then Scripts. Create a new script, paste the code, click Authorise, then Preview to check the logs, and schedule it Hourly.

Do that once and every script below is a two-minute install.

Script 1: Weather On/Off Campaign Toggler

What it does: checks the current and next-hour forecast, then enables labelled campaigns when their condition is met and pauses them when it isn’t. Run it hourly and your umbrella campaign switches itself on the moment rain is likely.

/**
 * Weather-triggered campaign toggler for Google Ads Scripts.
 * Data source: OpenWeatherMap One Call (Current + Hourly).
 *
 * Setup:
 * 1) Create labels on campaigns you want controlled, e.g. WT:Rain, WT:Hot
 * 2) Add your OpenWeatherMap API key and coordinates below
 * 3) Schedule hourly in Google Ads
 */

const CONFIG = {
  OPENWEATHER_API_KEY: 'YOUR_OWM_API_KEY',
  // Coordinates for the target location (Melbourne CBD example):
  LAT: -37.8136,
  LON: 144.9631,
  UNITS: 'metric', // 'metric' (°C), 'imperial' (°F)
  RAIN_PROB_THRESHOLD: 0.6, // 60% chance
  HOT_TEMP_C_THRESHOLD: 30,  // 30°C
  // Labels to look for on campaigns:
  LABELS: {
    RAIN: 'WT:Rain',
    HOT: 'WT:Hot'
  }
};

function main() {
  const weather = fetchWeather();
  const now = weather.current;
  const nextHour = weather.hourly && weather.hourly.length ? weather.hourly[0] : null;

  // Compute simple triggers
  // Use next-hour precipitation probability if available; fallback to 0.
  const pop = nextHour ? (nextHour.pop || 0) : 0; // probability of precip (0..1)
  const tempC = (now && typeof now.temp === 'number') ? now.temp : null;

  // RAIN logic
  toggleByLabel(
    CONFIG.LABELS.RAIN,
    (pop >= CONFIG.RAIN_PROB_THRESHOLD),
    'Rain (pop=' + Math.round(pop * 100) + '%)'
  );

  // HOT logic
  toggleByLabel(
    CONFIG.LABELS.HOT,
    (tempC !== null && tempC >= CONFIG.HOT_TEMP_C_THRESHOLD),
    'Hot (temp=' + tempC + '°C)'
  );
}

/**
 * Enable campaigns with label if condition true; otherwise pause them.
 */
function toggleByLabel(labelName, shouldEnable, reason) {
  const it = AdsApp.campaigns()
    .withCondition('LabelNames CONTAINS_ANY ["' + labelName + '"]')
    .withCondition("Status != REMOVED")
    .get();

  const actions = { enabled: 0, paused: 0 };

  while (it.hasNext()) {
    const c = it.next();
    const name = c.getName();

    if (shouldEnable) {
      if (!c.isEnabled()) {
        c.enable();
        Logger.log('ENABLED: ' + name + ' | ' + reason);
        actions.enabled++;
      }
    } else {
      if (!c.isPaused()) {
        c.pause();
        Logger.log('PAUSED: ' + name + ' | ' + reason);
        actions.paused++;
      }
    }
  }
  Logger.log('Label "' + labelName + '": enabled ' + actions.enabled + ', paused ' + actions.paused);
}

/**
 * Calls OpenWeatherMap One Call API (current + hourly).
 * Returns JSON with fields: current, hourly, etc.
 */
function fetchWeather() {
  const url = [
    'https://api.openweathermap.org/data/3.0/onecall',
    '?lat=', encodeURIComponent(CONFIG.LAT),
    '&lon=', encodeURIComponent(CONFIG.LON),
    '&units=', encodeURIComponent(CONFIG.UNITS),
    '&exclude=minutely,daily,alerts',
    '&appid=', encodeURIComponent(CONFIG.OPENWEATHER_API_KEY)
  ].join('');

  const res = UrlFetchApp.fetch(url, { 'muteHttpExceptions': true, 'method': 'get' });
  const code = res.getResponseCode();
  if (code < 200 || code >= 300) {
    throw new Error('OpenWeather error HTTP ' + code + ': ' + res.getContentText());
  }
  return JSON.parse(res.getContentText());
}

How to customise quickly:

  • Change LAT/LON to your target city (or use Script 3 for multiple cities).
  • Adjust RAIN_PROB_THRESHOLD and HOT_TEMP_C_THRESHOLD for your brand.
  • Swap UNITS to “imperial” if you prefer °F.

Script 2: Weather Budget Scaler

What it does: instead of switching campaigns on and off, this script scales daily budgets up when your weather condition hits and back down when it passes. Hard on/off switching can hurt campaigns that need consistent delivery; budget scaling keeps them live but leans into demand spikes.

You set each campaign’s normal daily budget in the config, and the script always calculates from that figure. Running it every hour can’t compound one multiplier on top of another. If a campaign uses a shared budget, scaling it changes the budget for every campaign that shares it.

/**
 * Weather-based budget scaler for Google Ads Scripts.
 * Scales labelled campaign budgets by a multiplier when a weather
 * condition is met, and restores the base budget when it is not.
 *
 * Setup:
 * 1) Label campaigns with WT:BudgetRain and/or WT:BudgetHot
 * 2) Add your OpenWeatherMap API key and coordinates below
 * 3) Set each labelled campaign's normal daily budget in BASE_BUDGETS
 * 4) Schedule hourly in Google Ads
 */

const CONFIG = {
  OPENWEATHER_API_KEY: 'YOUR_OWM_API_KEY',
  LAT: -37.8136,          // Melbourne CBD example
  LON: 144.9631,
  UNITS: 'metric',
  RAIN_PROB_THRESHOLD: 0.6,   // 60% chance of rain
  HOT_TEMP_C_THRESHOLD: 30,   // 30°C
  RAIN_BUDGET_MULTIPLIER: 1.5, // +50% budget when raining
  HOT_BUDGET_MULTIPLIER: 2.0,  // double budget in a heatwave
  // Normal daily budget for each labelled campaign, by exact campaign name
  BASE_BUDGETS: {
    'Rainwear - Melbourne': 40,
    'Cold drinks - Melbourne': 60
  },
  LABELS: {
    RAIN: 'WT:BudgetRain',
    HOT: 'WT:BudgetHot'
  }
};

function main() {
  const weather = fetchWeather();
  const nextHour = weather.hourly && weather.hourly.length ? weather.hourly[0] : null;
  const pop = nextHour ? (nextHour.pop || 0) : 0;
  const tempC = (weather.current && typeof weather.current.temp === 'number')
    ? weather.current.temp : null;

  scaleByLabel(
    CONFIG.LABELS.RAIN,
    pop >= CONFIG.RAIN_PROB_THRESHOLD,
    CONFIG.RAIN_BUDGET_MULTIPLIER,
    'Rain (pop=' + Math.round(pop * 100) + '%)'
  );

  scaleByLabel(
    CONFIG.LABELS.HOT,
    tempC !== null && tempC >= CONFIG.HOT_TEMP_C_THRESHOLD,
    CONFIG.HOT_BUDGET_MULTIPLIER,
    'Hot (temp=' + tempC + '°C)'
  );
}

/**
 * Scales budgets for campaigns carrying labelName.
 * The multiplier is always applied to the base budget in
 * CONFIG.BASE_BUDGETS, never to the current budget.
 */
function scaleByLabel(labelName, conditionMet, multiplier, reason) {
  const it = AdsApp.campaigns()
    .withCondition('LabelNames CONTAINS_ANY ["' + labelName + '"]')
    .withCondition('Status != REMOVED')
    .get();

  while (it.hasNext()) {
    const c = it.next();
    const base = CONFIG.BASE_BUDGETS[c.getName()];
    if (typeof base !== 'number') {
      Logger.log('SKIPPED: no base budget in CONFIG for ' + c.getName());
      continue;
    }

    const target = conditionMet
      ? Math.round(base * multiplier * 100) / 100
      : base;

    if (c.getBudget().getAmount() !== target) {
      c.getBudget().setAmount(target);
      Logger.log((conditionMet ? 'SCALED: ' : 'RESTORED: ') +
        c.getName() + ' to ' + target + ' | ' + reason);
    }
  }
}

function fetchWeather() {
  const url = [
    'https://api.openweathermap.org/data/3.0/onecall',
    '?lat=', encodeURIComponent(CONFIG.LAT),
    '&lon=', encodeURIComponent(CONFIG.LON),
    '&units=', encodeURIComponent(CONFIG.UNITS),
    '&exclude=minutely,daily,alerts',
    '&appid=', encodeURIComponent(CONFIG.OPENWEATHER_API_KEY)
  ].join('');

  const res = UrlFetchApp.fetch(url, { muteHttpExceptions: true, method: 'get' });
  const code = res.getResponseCode();
  if (code < 200 || code >= 300) {
    throw new Error('OpenWeather error HTTP ' + code + ': ' + res.getContentText());
  }
  return JSON.parse(res.getContentText());
}

When to use this instead of Script 1: campaigns running Smart Bidding, where reactivating a paused campaign restarts learning, or any campaign where you want weather to be a throttle rather than a switch.

Script 3: Multi-City Weather Toggler

What it does: the same logic as Script 1, but driven by a city table. Each city gets its own coordinates and label suffix (WT:Rain:Sydney, WT:Rain:Melbourne), so a national advertiser can run localised weather rules from a single script. One API call per city per run keeps you inside free-tier limits for most accounts.

/**
 * Multi-city weather toggler for Google Ads Scripts.
 * Each city has its own coordinates and label suffix, e.g.
 * WT:Rain:Sydney, WT:Hot:Melbourne.
 *
 * Setup:
 * 1) Label campaigns per city, e.g. WT:Rain:Sydney
 * 2) Add cities to the CITIES array below
 * 3) Schedule hourly in Google Ads
 */

const CONFIG = {
  OPENWEATHER_API_KEY: 'YOUR_OWM_API_KEY',
  UNITS: 'metric',
  RAIN_PROB_THRESHOLD: 0.6,
  HOT_TEMP_C_THRESHOLD: 30,
  CITIES: [
    { name: 'Sydney',    lat: -33.8688, lon: 151.2093 },
    { name: 'Melbourne', lat: -37.8136, lon: 144.9631 },
    { name: 'Brisbane',  lat: -27.4698, lon: 153.0251 }
  ]
};

function main() {
  CONFIG.CITIES.forEach(function (city) {
    const weather = fetchWeather(city.lat, city.lon);
    const nextHour = weather.hourly && weather.hourly.length ? weather.hourly[0] : null;
    const pop = nextHour ? (nextHour.pop || 0) : 0;
    const tempC = (weather.current && typeof weather.current.temp === 'number')
      ? weather.current.temp : null;

    toggleByLabel(
      'WT:Rain:' + city.name,
      pop >= CONFIG.RAIN_PROB_THRESHOLD,
      city.name + ' rain (pop=' + Math.round(pop * 100) + '%)'
    );

    toggleByLabel(
      'WT:Hot:' + city.name,
      tempC !== null && tempC >= CONFIG.HOT_TEMP_C_THRESHOLD,
      city.name + ' hot (temp=' + tempC + '°C)'
    );
  });
}

function toggleByLabel(labelName, shouldEnable, reason) {
  const it = AdsApp.campaigns()
    .withCondition('LabelNames CONTAINS_ANY ["' + labelName + '"]')
    .withCondition('Status != REMOVED')
    .get();

  while (it.hasNext()) {
    const c = it.next();
    if (shouldEnable && !c.isEnabled()) {
      c.enable();
      Logger.log('ENABLED: ' + c.getName() + ' | ' + reason);
    } else if (!shouldEnable && !c.isPaused()) {
      c.pause();
      Logger.log('PAUSED: ' + c.getName() + ' | ' + reason);
    }
  }
}

function fetchWeather(lat, lon) {
  const url = [
    'https://api.openweathermap.org/data/3.0/onecall',
    '?lat=', encodeURIComponent(lat),
    '&lon=', encodeURIComponent(lon),
    '&units=', encodeURIComponent(CONFIG.UNITS),
    '&exclude=minutely,daily,alerts',
    '&appid=', encodeURIComponent(CONFIG.OPENWEATHER_API_KEY)
  ].join('');

  const res = UrlFetchApp.fetch(url, { muteHttpExceptions: true, method: 'get' });
  const code = res.getResponseCode();
  if (code < 200 || code >= 300) {
    throw new Error('OpenWeather error HTTP ' + code + ': ' + res.getContentText());
  }
  return JSON.parse(res.getContentText());
}

Script 4: Weather Performance Logger

What it does: once an hour, appends a row to a Google Sheet with the current temperature, rain probability, and your labelled campaigns’ spend, clicks, and conversions for the day. After a few weeks you have the dataset that answers the only question that matters: does weather actually move your numbers? Pair it with our guide to measuring weather ad ROI.

/**
 * Weather performance logger for Google Ads Scripts.
 * Appends one row per run to a Google Sheet: timestamp, temp,
 * rain probability, and today's spend/clicks/conversions for
 * each labelled campaign.
 *
 * Setup:
 * 1) Create a blank Google Sheet and paste its URL below
 * 2) Label the campaigns you want tracked with WT:Log
 * 3) Schedule hourly in Google Ads
 */

const CONFIG = {
  OPENWEATHER_API_KEY: 'YOUR_OWM_API_KEY',
  LAT: -37.8136,
  LON: 144.9631,
  UNITS: 'metric',
  SHEET_URL: 'https://docs.google.com/spreadsheets/d/YOUR_SHEET_ID/edit',
  LABEL: 'WT:Log'
};

function main() {
  const weather = fetchWeather();
  const nextHour = weather.hourly && weather.hourly.length ? weather.hourly[0] : null;
  const pop = nextHour ? (nextHour.pop || 0) : 0;
  const tempC = (weather.current && typeof weather.current.temp === 'number')
    ? weather.current.temp : null;

  const sheet = SpreadsheetApp.openByUrl(CONFIG.SHEET_URL).getActiveSheet();
  if (sheet.getLastRow() === 0) {
    sheet.appendRow(['Timestamp', 'Temp C', 'Rain prob %', 'Campaign',
      'Cost today', 'Clicks today', 'Conversions today']);
  }

  const it = AdsApp.campaigns()
    .withCondition('LabelNames CONTAINS_ANY ["' + CONFIG.LABEL + '"]')
    .withCondition('Status != REMOVED')
    .get();

  while (it.hasNext()) {
    const c = it.next();
    const stats = c.getStatsFor('TODAY');
    sheet.appendRow([
      new Date(),
      tempC,
      Math.round(pop * 100),
      c.getName(),
      stats.getCost(),
      stats.getClicks(),
      stats.getConversions()
    ]);
  }
}

function fetchWeather() {
  const url = [
    'https://api.openweathermap.org/data/3.0/onecall',
    '?lat=', encodeURIComponent(CONFIG.LAT),
    '&lon=', encodeURIComponent(CONFIG.LON),
    '&units=', encodeURIComponent(CONFIG.UNITS),
    '&exclude=minutely,daily,alerts',
    '&appid=', encodeURIComponent(CONFIG.OPENWEATHER_API_KEY)
  ].join('');

  const res = UrlFetchApp.fetch(url, { muteHttpExceptions: true, method: 'get' });
  const code = res.getResponseCode();
  if (code < 200 || code >= 300) {
    throw new Error('OpenWeather error HTTP ' + code + ': ' + res.getContentText());
  }
  return JSON.parse(res.getContentText());
}

Scripts are free and flexible. Platforms cost money and save time. The honest comparison:

Google Ads scriptsAutomation platform (e.g. WeatherTrigger)
CostFree (plus weather API fees at scale)Monthly subscription
SetupCopy, paste, authorise, scheduleConnect account, set rules in a UI
MaintenanceYou own it: API changes, errors, monitoringHandled for you
ChannelsGoogle Ads onlyWeatherTrigger: Meta today, Google Ads coming soon
Complex rulesAnything you can codeAND/OR conditions, no code
Failure visibilityCheck logs yourselfLog of every check and change

Short answer: if you run one or two Google campaigns in one city, use the scripts on this page. If you manage weather rules across many campaigns and cities, a platform saves maintenance time. WeatherTrigger does this for Meta ads today, with Google Ads support coming soon. For other Google Ads options, see our comparison of weather ad tools.

Common Gotchas (and Fixes)

  • API limits: free weather API tiers cap requests. One call per hour per city is fine; per-campaign calls are not.
  • Flip-flopping: weather changes fast. Add hysteresis: require two consecutive “true” checks before enabling and two “false” checks before pausing, storing the last state in a Google Sheet (as Script 4 does).
  • Smart Bidding resets: reactivating a paused campaign restarts bid-strategy learning. Prefer Script 2 (budget scaling) for Smart Bidding campaigns. Budget changes don’t reset learning.
  • Time zones: scripts run in your account’s time zone, but forecasts are location-based. Multi-city advertisers should use Script 3’s per-city coordinates.
  • Permissions: the authorising user needs edit rights on campaigns and labels.

Frequently Asked Questions

Are Google Ads scripts free?

Yes. Scripts run inside your Google Ads account at no cost. The only potential expense is the weather API. OpenWeatherMap’s One Call API 3.0 includes 1,000 free calls a day, enough for hourly checks across dozens of locations, but it needs a card on file.

How often can Google Ads scripts run?

The minimum schedule is hourly, which is exactly right for weather automation. Each execution can run for up to 30 minutes.

Do these scripts work with Performance Max?

Not as written. AdsApp.campaigns() covers Search and Display campaigns but not Performance Max, Shopping or Video. For Performance Max, change the selector to AdsApp.performanceMaxCampaigns(), which can pause, enable and change budgets, then check the result with Preview. Scripts can also reach asset groups through AdsApp.performanceMaxCampaigns().assetGroups() if you want to switch weather-specific creative instead of the whole campaign.

Can Google Ads scripts adjust bids based on weather?

Only on manual CPC campaigns. Smart Bidding ignores location and ad schedule bid adjustments and sets its own bids, so for most campaigns budget scaling (Script 2) or changing CPA and ROAS targets is the lever that works. See weather targeting for Google Ads.

What about Meta ads?

Google Ads scripts only work inside Google Ads. For Meta, see our guide to weather-triggered Facebook ads with Make.com, or try WeatherTrigger, which switches Meta ad sets by weather with no code.

Get the Scripts + Setup Checklist by Email

Want all four scripts plus our 10-minute setup checklist in your inbox? Enter your email below. We’ll also let you know when WeatherTrigger adds Google Ads support.