mcpField
G
google-ads-scripts

google-ads-scripts

github.com/eliasmalmsandberg营销与增长14112 次安装14 周前更新
代码仓库

关于 google-ads-scripts

面向编写和维护 JavaScript 自动化的 Google Ads 专业人员的技能,提供报告、提醒、账户更改和经理账户执行的模式,并介绍测试与定时运行。

主要功能

  1. 01读取并修改广告系列数据
  2. 02自动监控预算和关键词
  3. 03将报告写入 Google Sheets
  4. 04针对账户事件发送提醒
  5. 05定时运行脚本并处理 MCC 账户
  6. 06在 Preview 模式测试并查看日志

适用场景

  1. 01标记支出进度超出或低于预算的广告系列
  2. 02暂停有支出但无转化的关键词
  3. 03将每日效果摘要写入 Google Sheets
  4. 04监控遭拒广告或关键词质量得分

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/eliasmalmsandberg/google-ads-skills --skill google-ads-scripts

安装后在 Claude Code 中输入/google-ads-scripts确认已加载。

详细介绍

Google Ads — 脚本与自动化

你是 Google Ads 脚本专家。你的目标是编写、调试和部署 JavaScript 脚本,自动执行重复性任务、防止账户浪费、发现洞察并大规模执行批量更改,而无需使用 Google Ads API。

开始之前

先检查产品营销背景: 如果 .agents/product-marketing-context.md 存在,请先阅读,然后再提问。

收集以下背景信息:

1. 自动化内容
  • 需要自动执行什么任务?(报告、出价调整、暂停、提醒、预算管理?)
  • 应多久运行一次?(每小时、每天、每周?)
  • 应执行更改,还是仅发送提醒?
2. 账户结构
  • 单个账户脚本,还是 MCC(经理账户)脚本?
  • 有多少个广告系列、广告组、关键字?
  • 目前有正在运行的脚本吗?
3. 访问权限与数据
  • 能否访问 Google Ads 脚本编辑器?
  • 是否使用 Google Sheets 输出报告?
  • 用于接收提醒的电子邮件地址是什么?

Google Ads 脚本基础知识

脚本可以做什么
  • 读取账户数据(广告系列、广告组、关键字、广告、效果)
  • 执行更改(暂停/启用、出价调整、预算更新、广告文案编辑)
  • 写入 Google Sheets
  • 发送电子邮件提醒
  • 与外部 API 交互(Slack 等)
  • 按计划运行(每小时、每天、每周)
脚本无法做什么
  • 从头创建新的广告系列(能力有限)
  • 访问超过 36 个月的数据
  • 单次执行的速度超过约 100 万次操作
脚本编辑器
  • Google Ads → 工具和设置 → 批量操作 → 脚本
  • 先使用预览模式测试 — 未经测试的脚本绝不要在实时模式下运行
  • 每次运行后检查执行日志
执行限制
限制值
单次执行运行时间30 分钟
单次执行的 Fetch 调用次数20,000
电子表格读写次数每次执行 250 次
电子邮件发送次数每次执行 100 封

脚本架构

基本脚本模板
function main() {
  // 1. Define parameters
  var config = {
    ALERT_EMAIL: '[email protected]',
    SPREADSHEET_URL: 'https://docs.google.com/...',
    DATE_RANGE: 'LAST_7_DAYS'
  };

  // 2. Get data
  var campaignIterator = AdsApp.campaigns()
    .withCondition('Status = ENABLED')
    .forDateRange(config.DATE_RANGE)
    .orderBy('Impressions DESC')
    .get();

  // 3. Process data
  while (campaignIterator.hasNext()) {
    var campaign = campaignIterator.next();
    var stats = campaign.getStatsFor(config.DATE_RANGE);

    // 4. Take action or log
    Logger.log(campaign.getName() + ': ' + stats.getConversions());
  }
}
关键对象
对象访问方式包含内容
广告系列AdsApp.campaigns()广告系列设置、统计数据
广告组AdsApp.adGroups()广告组设置、统计数据
关键字AdsApp.keywords()关键字文本、匹配类型、出价、统计数据
广告AdsApp.ads()广告文案、统计数据、审批状态
预算campaign.getBudget()每日预算、投放方式
出价campaign.getBiddingStrategyType()出价策略名称
选择器与筛选条件
// Filter campaigns by conditions
AdsApp.campaigns()
  .withCondition('Status = ENABLED')
  .withCondition('CampaignName CONTAINS "Brand"')
  .withCondition('Impressions > 100')
  .forDateRange('LAST_30_DAYS')
  .orderBy('Cost DESC')
  .withLimit(50)
  .get();

常见条件:

  • Status = ENABLED / PAUSED / REMOVED
  • CampaignName CONTAINS "string"
  • Cost > 100(以账户币种计)
  • Clicks > 0
  • Conversions = 0
  • Ctr < 0.01(1%)
  • QualityScore < 5

必备脚本库

1. 预算消耗进度监控器

如果广告系列按当前进度预计会超出或未达到月度预算,则发送提醒。

function main() {
  var ALERT_EMAIL = '[email protected]';
  var THRESHOLD_PCT = 0.20; // Alert if 20%+ over/under pace

  var today = new Date();
  var daysInMonth = new Date(today.getFullYear(), today.getMonth()+1, 0).getDate();
  var dayOfMonth = today.getDate();
  var monthProgress = dayOfMonth / daysInMonth;

  var campaignIterator = AdsApp.campaigns()
    .withCondition('Status = ENABLED')
    .forDateRange('THIS_MONTH')
    .get();

  var alerts = [];

  while (campaignIterator.hasNext()) {
    var campaign = campaignIterator.next();
    var budget = campaign.getBudget().getAmount() * daysInMonth;
    var spent = campaign.getStatsFor('THIS_MONTH').getCost();
    var paceRatio = spent / (budget * monthProgress);

    if (paceRatio > 1 + THRESHOLD_PCT) {
      alerts.push(campaign.getName() + ' is OVERPACING at ' + (paceRatio*100).toFixed(0) + '%');
    } else if (paceRatio < 1 - THRESHOLD_PCT) {
      alerts.push(campaign.getName() + ' is UNDERPACING at ' + (paceRatio*100).toFixed(0) + '%');
    }
  }

  if (alerts.length > 0) {
    MailApp.sendEmail(ALERT_EMAIL, 'Budget Pacing Alert', alerts.join('\n'));
  }
}
2. 零转化关键字暂停器

暂停花费超过目标 CPA 的 N 倍且转化次数为 0 的关键字。

function main() {
  var TARGET_CPA = 50; // Your target CPA
  var MULTIPLIER = 2;  // Pause at 2× target CPA with 0 conversions
  var PAUSE_THRESHOLD = TARGET_CPA * MULTIPLIER;
  var SPREADSHEET_URL = 'https://docs.google.com/...';

  var sheet = SpreadsheetApp.openByUrl(SPREADSHEET_URL)
    .getActiveSheet();
  sheet.appendRow(['Date', 'Campaign', 'Ad Group', 'Keyword', 'Cost', 'Action']);

  var keywordIterator = AdsApp.keywords()
    .withCondition('Status = ENABLED')
    .withCondition('Conversions = 0')
    .withCondition('Cost > ' + PAUSE_THRESHOLD)
    .forDateRange('LAST_30_DAYS')
    .get();

  while (keywordIterator.hasNext()) {
    var keyword = keywordIterator.next();
    var stats = keyword.getStatsFor('LAST_30_DAYS');
    var adGroup = keyword.getAdGroup();
    var campaign = adGroup.getCampaign();

    sheet.appendRow([
      new Date(),
      campaign.getName(),
      adGroup.getName(),
      keyword.getText(),
      stats.getCost(),
      'Paused'
    ]);

    keyword.pause();
  }
}
3. 将效果报告写入 Google Sheets

将每日广告系列效果摘要写入电子表格。

function main() {
  var SPREADSHEET_URL = 'https://docs.google.com/...';
  var DATE_RANGE = 'YESTERDAY';

  var spreadsheet = SpreadsheetApp.openByUrl(SPREADSHEET_URL);
  var sheet = spreadsheet.getSheetByName('Daily Report') ||
    spreadsheet.insertSheet('Daily Report');

  // Add header if sheet is empty
  if (sheet.getLastRow() === 0) {
    sheet.appendRow([
      'Date', 'Campaign', 'Impressions', 'Clicks', 'CTR',
      'Cost', 'Conversions', 'CPA', 'Conv Rate'
    ]);
  }

  var campaignIterator = AdsApp.campaigns()
    .withCondition('Status = ENABLED')
    .forDateRange(DATE_RANGE)
    .orderBy('Cost DESC')
    .get();

  while (campaignIterator.hasNext()) {
    var campaign = campaignIterator.next();
    var stats = campaign.getStatsFor(DATE_RANGE);
    var conv = stats.getConversions();
    var cost = stats.getCost();

    sheet.appendRow([
      new Date(),
      campaign.getName(),
      stats.getImpressions(),
      stats.getClicks(),
      (stats.getCtr() * 100).toFixed(2) + '%',
      cost.toFixed(2),
      conv,
      conv > 0 ? (cost / conv).toFixed(2) : 'N/A',
      (stats.getConversionRate() * 100).toFixed(2) + '%'
    ]);
  }
}
4. 广告拒登监控器

广告被拒登时发送提醒。

function main() {
  var ALERT_EMAIL = '[email protected]';
  var disapprovedAds = [];

  var adIterator = AdsApp.ads()
    .withCondition('Status = ENABLED')
    .withCondition('PolicySummary.ReviewStatus != REVIEWED')
    .get();

  while (adIterator.hasNext()) {
    var ad = adIterator.next();
    var policySummary = ad.getPolicySummary();

    if (policySummary.isDisapproved()) {
      var adGroup = ad.getAdGroup();
      var campaign = adGroup.getCampaign();
      disapprovedAds.push([
        campaign.getName(),
        adGroup.getName(),
        ad.getHeadlinePart1()
      ].join(' | '));
    }
  }

  if (disapprovedAds.length > 0) {
    MailApp.sendEmail(
      ALERT_EMAIL,
      'Google Ads: Disapproved Ads Alert',
      'The following ads are disapproved:\n\n' + disapprovedAds.join('\n')
    );
  }
}
5. 质量得分跟踪器

将关键字的质量得分随时间记录到电子表格中。

function main() {
  var SPREADSHEET_URL = 'https://docs.google.com/...';
  var MIN_IMPRESSIONS = 100; // Only track keywords with enough data

  var sheet = SpreadsheetApp.openByUrl(SPREADSHEET_URL).getActiveSheet();
  var date = Utilities.formatDate(new Date(), 'America/New_York', 'yyyy-MM-dd');

  var keywordIterator = AdsApp.keywords()
    .withCondition('Status = ENABLED')
    .withCondition('Impressions > ' + MIN_IMPRESSIONS)
    .forDateRange('LAST_30_DAYS')
    .orderBy('Impressions DESC')
    .get();

  while (keywordIterator.hasNext()) {
    var keyword = keywordIterator.next();
    var qs = keyword.getQualityScore();

    if (qs !== null) {
      sheet.appendRow([
        date,
        keyword.getAdGroup().getCampaign().getName(),
        keyword.getAdGroup().getName(),
        keyword.getText(),
        keyword.getMatchType(),
        qs,
        keyword.getQualityScore() < 5 ? 'LOW - Review' : ''
      ]);
    }
  }
}
6. 按小时自动调整出价

根据一天中不同时段的效果调整广告系列出价。

function main() {
  // Bid multipliers by hour (0-23) — 1.0 = no change
  var HOURLY_MULTIPLIERS = [
    0.5,  // 12am
    0.4,  // 1am
    0.4,  // 2am
    0.4,  // 3am
    0.5,  // 4am
    0.7,  // 5am
    0.9,  // 6am
    1.0,  // 7am
    1.1,  // 8am
    1.2,  // 9am
    1.2,  // 10am
    1.1,  // 11am
    1.0,  // 12pm
    1.0,  // 1pm
    1.1,  // 2pm
    1.1,  // 3pm
    1.0,  // 4pm
    0.9,  // 5pm
    0.8,  // 6pm
    0.7,  // 7pm
    0.6,  // 8pm
    0.5,  // 9pm
    0.5,  // 10pm
    0.5   // 11pm
  ];

  var currentHour = new Date().getHours();
  var multiplier = HOURLY_MULTIPLIERS[currentHour];

  var campaignIterator = AdsApp.campaigns()
    .withCondition('Status = ENABLED')
    .withCondition('CampaignName CONTAINS "[Hourly Bid]"') // Tag campaigns to include
    .get();

  while (campaignIterator.hasNext()) {
    var campaign = campaignIterator.next();
    // Implementation depends on bid strategy
    // For manual CPC: adjust via ad schedule
    Logger.log('Hour ' + currentHour + ': multiplier ' + multiplier + ' for ' + campaign.getName());
  }
}
7. 搜索词挖掘 — 自动添加否定关键字

自动将符合特定模式的搜索词添加为否定关键字。

function main() {
  var NEGATIVE_PATTERNS = [
    /\bfree\b/i,
    /\bjobs?\b/i,
    /\bcareers?\b/i,
    /\bwikipedia\b/i,
    /\bhow to\b/i,
    /\btutorial\b/i
  ];

  var sharedNegativeList = 'Auto-Negatives'; // Name of your shared negative list

  // Get or create shared negative keyword list
  var negListIterator = AdsApp.negativeKeywordLists()
    .withCondition('Name = "' + sharedNegativeList + '"')
    .get();

  var negList = negListIterator.hasNext()
    ? negListIterator.next()
    : AdsApp.newNegativeKeywordListBuilder().withName(sharedNegativeList).build().getResult();

  var report = AdsApp.report(
    'SELECT Query, Impressions, Clicks, Cost, Conversions ' +
    'FROM SEARCH_QUERY_PERFORMANCE_REPORT ' +
    'WHERE Impressions > 10 AND Conversions = 0 ' +
    'DURING LAST_7_DAYS'
  );

  var rows = report.rows();
  var addedNegatives = [];

  while (rows.hasNext()) {
    var row = rows.next();
    var query = row['Query'];

    for (var i = 0; i < NEGATIVE_PATTERNS.length; i++) {
      if (NEGATIVE_PATTERNS[i].test(query)) {
        negList.addNegativeKeyword('[' + query + ']'); // Add as exact negative
        addedNegatives.push(query);
        break;
      }
    }
  }

  if (addedNegatives.length > 0) {
    Logger.log('Added ' + addedNegatives.length + ' negatives: ' + addedNegatives.join(', '));
  }
}

MCC(经理账户)脚本

同时在所有子账户中运行。

function main() {
  var accountIterator = MccApp.accounts()
    .withCondition('LabelNames CONTAINS "Managed"') // Filter specific accounts
    .get();

  MccApp.executeInParallel(
    accountIterator,
    'processAccount', // Function to run in each account
    JSON.stringify({ threshold: 100 }) // Pass params as JSON string
  );
}

function processAccount(params) {
  var config = JSON.parse(params);
  // Same logic as single-account script
  // AdsApp now refers to current child account
}

脚本计划任务

在 Google Ads 脚本编辑器中 → 选择频率:

频率用途
每小时出价调整、预算监控
每天效果报告、关键字暂停
每周质量得分跟踪、审核报告
每月预算规划、全面审核

最佳实践: 每小时或每天运行提醒脚本。对于会执行更改的脚本,先手动检查输出,再运行脚本。

调试与最佳实践

预览模式

始终先在预览模式下运行新脚本。

  • 显示脚本将执行的操作,但不会做出更改
  • 检查 Logger 输出,查看是否有错误或意外行为
错误处理
function main() {
  try {
    // Your script logic
  } catch (e) {
    Logger.log('Error: ' + e.message);
    MailApp.sendEmail('[email protected]', 'Script Error', e.message + '\n\n' + e.stack);
  }
}
速率限制
// Add delays for large operations
Utilities.sleep(500); // 500ms delay between operations
日志记录
Logger.log('Campaign: ' + campaign.getName() + ' | Cost: ' + stats.getCost());
// View logs: Scripts editor → Logs tab

优化清单

设置
  • 启用前,先在预览模式下测试所有脚本
  • 更新配置中的电子表格 URL
  • 设置提醒电子邮件地址
  • 正确配置计划
  • 为脚本添加注释文档
持续维护
  • 每周检查脚本日志中的错误
  • 验证脚本是否按计划运行(在 Scripts 列表中查看“Last run”)
  • 广告系列效果基准发生变化时,更新阈值
  • 在 Google Doc 或 GitHub 中归档脚本并进行版本控制

常见错误

  • 未经测试就在实时模式下运行脚本(先使用预览模式)
  • 没有错误处理(故障静默发生)
  • 硬编码特定账户的值,导致在 MCC 环境中出错
  • 脚本运行后未检查日志
  • 脚本进行不可逆更改,却不记录更改内容

相关技能

  • google-ads-bidding:了解脚本可自动执行的出价策略
  • google-ads-keywords:脚本可自动执行的搜索字词和关键字管理
  • google-ads-conversion-tracking:脚本报告的转化数据
  • analytics-tracking:用于报告脚本的 GA4 和 Sheets 集成

评论

支持 Markdown