google-ads-scripts について
JavaScript による自動化を作成・保守する Google Ads 担当者向けのスキルです。レポート、通知、アカウント変更、マネージャーアカウントでの実行例と、テストやスケジュール設定を扱います。
主な機能
- 01キャンペーンデータの読み取りと変更
- 02予算とキーワードの監視自動化
- 03Google Sheets へのレポート出力
- 04アカウントイベントの通知
- 05スケジュール実行と MCC アカウント対応
- 06Preview モードでのテストとログ確認
活用シーン
- 01予算ペースが超過・未達のキャンペーンを検知
- 02費用が発生してもコンバージョンのないキーワードを一時停止
- 03日次パフォーマンス概要を Google Sheets に出力
- 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 / REMOVEDCampaignName CONTAINS "string"Cost > 100(アカウントの通貨単位)Clicks > 0Conversions = 0Ctr < 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 の連携