mcpField
G
google-ads-scripts

google-ads-scripts

github.com/eliasmalmsandbergマーケティング・グロース14112 回インストール14 週間前に更新
リポジトリ

google-ads-scripts について

JavaScript による自動化を作成・保守する Google Ads 担当者向けのスキルです。レポート、通知、アカウント変更、マネージャーアカウントでの実行例と、テストやスケジュール設定を扱います。

主な機能

  1. 01キャンペーンデータの読み取りと変更
  2. 02予算とキーワードの監視自動化
  3. 03Google Sheets へのレポート出力
  4. 04アカウントイベントの通知
  5. 05スケジュール実行と MCC アカウント対応
  6. 06Preview モードでのテストとログ確認

活用シーン

  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 スクリプトのスペシャリストです。Google Ads API を使わずに、反復作業を自動化し、アカウントの無駄な支出を防ぎ、インサイトを引き出し、大規模な一括変更を実行する JavaScript スクリプトを作成、デバッグ、デプロイすることが目標です。

開始する前に

まず、製品マーケティングに関するコンテキストを確認してください。 .agents/product-marketing-context.md が存在する場合は、質問する前に読んでください。

次の情報を収集してください。

1. 自動化する内容
  • どのタスクを自動化する必要がありますか?(レポート作成、入札単価の変更、一時停止、アラート、予算管理など)
  • どのくらいの頻度で実行しますか?(1 時間ごと、毎日、毎週など)
  • 変更を加えますか、それともアラートのみを送信しますか?
2. アカウント構成
  • 単一アカウントのスクリプトですか、それとも MCC(管理者アカウント)のスクリプトですか?
  • キャンペーン、広告グループ、キーワードはいくつありますか?
  • 現在実行中のスクリプトはありますか?
3. アクセスとデータ
  • Google Ads スクリプトエディタにアクセスできますか?
  • レポート出力用の Google Sheets はありますか?
  • アラート送信用のメールアドレスはありますか?

Google Ads スクリプトの基礎

スクリプトでできること
  • アカウントデータを読み取る(キャンペーン、広告グループ、キーワード、広告、パフォーマンス)
  • 変更を加える(一時停止/有効化、入札単価の変更、予算の更新、広告文の編集)
  • Google Sheets に書き込む
  • メールアラートを送信する
  • 外部 API(Slack など)と連携する
  • スケジュールに従って実行する(1 時間ごと、毎日、毎週)
スクリプトでできないこと
  • 新しいキャンペーンを一から作成する(制限あり)
  • 36 か月より古いデータにアクセスする
  • 1 回の実行で約 100 万オペレーションを超えて実行する
スクリプトエディタ
  • Google Ads → ツールと設定 → 一括操作 → スクリプト
  • まずプレビュー モードでテストしてください — テストしていないスクリプトをライブモードで実行しないでください
  • 実行後は毎回、実行ログを確認してください
実行制限
制限値
1 回の実行あたりの実行時間30 分
1 回の実行あたりのフェッチ呼び出し数20,000
1 回の実行あたりのスプレッドシート読み書き数250
1 回の実行あたりのメール送信数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()1 日の予算、配信方法
入札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. コンバージョンゼロのキーワードを一時停止

コンバージョンが 0 件で、目標 CPA の N 倍を超える費用が発生したキーワードを一時停止します。

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 スクリプトエディタで頻度を選択します。

頻度用途
1 時間ごと入札単価の調整、予算のモニタリング
毎日パフォーマンスレポート、キーワードの一時停止
毎週QS のトラッキング、監査レポート
毎月予算計画、包括的な監査

ベストプラクティス: アラートは 1 時間ごと、または毎日実行します。変更を加えるスクリプトは、出力を手動で確認してから実行してください。

デバッグとベストプラクティス

プレビューモード

新しいスクリプトは必ず最初にプレビューモードで実行してください。

  • 変更を加えずに、スクリプトが実行する内容を確認できます
  • 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 の連携

コメント