毎朝、AdSenseの管理画面を開いて「昨日の収益」と「今日の途中経過」を確認して、ついでにGA(GA4)も見て……というルーティンをやっている人は多いかと。
僕もそうでした。
やること自体は簡単なんだけど「毎日アクセスする」ってだけで地味に面倒なんですよね。
そこで今回は、AdSense と GA4 の数字を GAS(Google Apps Script)で取得して、朝と夕にメール送信する仕組みを作りました。
• 朝 7時:昨日の確定寄りの数字+今日の途中経過
• 夕 18時:今日の途中経過(伸び)を確認
メール内容は同じなんだけど、こんな用途で確認しております。
この記事では、できるだけコピペで動かせることを重視して、手順とコードをまとめてみます。
はじめに|この記事でできること
下記内容を定期的(毎日 7時 / 18時)に自動メール送信します。
- AdSense(推定収益)
- 本日(現時点)
- 昨日(先週同曜日比較)
- 今月(前年同月比較)
- GA4(ユーザー / PV相当)
- 全サイト合算
- サイト別(複数)
もちろん、トリガーの時間はお好みで設定可能。
メール内容
AdSense / GA デイリーレポート
2026-01-03 18:52
────────────────────────
■ Google AdSense
────────────────────────
◯ 本日(現時点まで)
¥1,000
◯ 昨日
¥1,200(先週同曜日比:▲+¥100)
◯ 今月(1日〜本日)
¥3,600(前年同期比:▲+¥500)
────────────────────────
■ Google Analytics(GA4)
────────────────────────
◯ 全サイト
【本日】
ユーザー数:10,000 PV数:15,000
【昨日】
ユーザー数:10,400(前年同日比:▲+1,000) PV数:17,000(前年同日比:▲+2,000)
◯ test.jp
【本日】
ユーザー数:3,000 PV数:5,000
【昨日】
ユーザー数:3,200(前年同日比:▲+300) PV数:6,000(前年同日比:▲+800)
◯ foo.com
【本日】
ユーザー数:3,000 PV数:5,000
【昨日】
ユーザー数:3,200(前年同日比:▲+300) PV数:6,000(前年同日比:▲+800)
◯ demo.site
【本日】
ユーザー数:4,000 PV数:5,000
【昨日】
ユーザー数:4,000(前年同日比:▲+400) PV数:5,000(前年同日比:▲+400)
────────────────────────
※ ▲ はプラス、▼ はマイナスを示します
※ 本日は現時点までの速報値です
※ 本メールは自動送信されています
────────────────────────
こんなメールが飛んできます。
ざっくり全体構成
- GCPプロジェクトを作る
- OAuth同意画面を設定する
- APIを有効化する
- AdSense Management API
- Google Analytics Data API(GA4)
- Apps Script を GCP に紐付ける
- appsscript.json にスコープを追加する
- スクリプトプロパティを設定(送信先・GAプロパティ一覧)
- コードを貼る → 実行 → 承認
- トリガーを設定
実装方法
1. GCPプロジェクト作成(Cloud Console)
Cloud Console で新規プロジェクトを作ります。
- Cloud Console → プロジェクト選択 → 新しいプロジェクト
- 任意の名前で作成
作ったら、右上などに出る プロジェクト番号(数字) を確認して控えておく。
Apps Script 側で「プロジェクト番号(数字)」を使います。
2. OAuth 同意画面
Cloud Console → API とサービス → OAuth 同意画面
- ユーザータイプ:External(外部)
- アプリ名:任意
- サポートメール:自分
- 開発者の連絡先:自分のメール
「テスト中」のままで。
個人利用なら、基本はこの方針でOKかと。
テストユーザーに今回利用する自分のGoogleアカウントを追加しておきます。
- OAuth同意画面 → テストユーザー → 追加 → 自分のGoogleアカウント
※注意
AdSenseの管理アカウントと、GASを実行するアカウントが違うと、Adsenseの情報が取得できません。
3. API を有効化
Cloud Console → API とサービス → ライブラリ
次の2つを有効化します。
• AdSense Management API
• Google Analytics Data API(GA4)
4. Apps Script を GCP プロジェクトに紐付ける
Apps Script エディタ(https://script.google.com/)を開きます。
- 左下の歯車「プロジェクト設定」
- 「Google Cloud Platform(GCP)プロジェクト」→ ここにCloud Consoleで作成したプロジェクトの「プロジェクト番号(数字) 」を入力して紐付け
5. Apps Script 側:高度なGoogleサービスをON(AdSense)
Apps Script エディタの左メニュー「サービス」から追加します。
- AdSense(v2) を追加
GA4(Analytics Data API)は、この記事では UrlFetch で直接叩くので、サービス追加は必須ではありません(入っていてもOK)。
6. appsscript.json(スコープ設定)
エディタで appsscript.json を編集します。
プロジェクト設定で「appsscript.json を表示」をONにすると、エディタの左メニューにappsscript.jsonが表示されるので、そこから編集できます。
以下のように oauthScopes を追加してください(dependenciesなどは既存のままでOK)。
{
"timeZone": "Asia/Tokyo",
"dependencies": {
"enabledAdvancedServices": [
{
"userSymbol": "AdSense",
"version": "v2",
"serviceId": "adsense"
}
]
},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"oauthScopes": [
"https://www.googleapis.com/auth/adsense.readonly",
"https://www.googleapis.com/auth/analytics.readonly",
"https://www.googleapis.com/auth/script.send_mail",
"https://www.googleapis.com/auth/script.external_request"
]
}
- script.send_mail:MailAppでメールを送る
- script.external_request:UrlFetchでGA4 APIを叩く
- adsense.readonly:AdSense取得
- analytics.readonly:GA4取得
7. スクリプトプロパティ設定(送信先・GAプロパティ)
秘匿性の高い情報はコードには書かず、スクリプトプロパティから取得します。
Apps Script → プロジェクト設定 → スクリプトプロパティ で設定。
REPORT_TO(任意)
送信先メール。
未設定なら自分(GASにログインしているアカウント)に送ります。
- REPORT_TO = your@email.com
GA_PROPERTIES(必須)
GA4プロパティIDをJSONで持たせます。
複数サイト対応の要。
[
{"label":"test.jp","propertyId":"123456789"},
{"label":"foo.com","propertyId":"123456790"},
{"label":"demo.site","propertyId":"123456791"}
]
※ propertyId は GA4 の「管理」→「プロパティ設定」で確認できます。
8. コピペで動くGASコード(AdSense + GA4 + メール)
以下を コード.gs に貼ります。
function sendDailyReport() {
const tz = Session.getScriptTimeZone() || 'Asia/Tokyo';
const now = new Date();
const to = getReportTo_();
const dToday = formatDate_(now, tz);
const dYesterday = formatDate_(addDays_(now, -1), tz);
const dLastWeekSameDay = formatDate_(addDays_(now, -8), tz); // 昨日の先週同曜日
const monthStart = formatDate_(new Date(now.getFullYear(), now.getMonth(), 1), tz);
const lastYearToday = new Date(now);
lastYearToday.setFullYear(now.getFullYear() - 1);
const lastYearMonthStart = formatDate_(new Date(now.getFullYear() - 1, now.getMonth(), 1), tz);
const lastYearTodayStr = formatDate_(lastYearToday, tz);
const lastYearYesterday = new Date(addDays_(now, -1));
lastYearYesterday.setFullYear(lastYearYesterday.getFullYear() - 1);
const lastYearYesterdayStr = formatDate_(lastYearYesterday, tz);
// --- AdSense ---
const adsToday = adsenseEarnings_(dToday, dToday);
const adsYesterday = adsenseEarnings_(dYesterday, dYesterday);
const adsLastWeekSame = adsenseEarnings_(dLastWeekSameDay, dLastWeekSameDay);
const adsThisMonth = adsenseEarnings_(monthStart, dToday);
const adsThisMonthLastYear = adsenseEarnings_(lastYearMonthStart, lastYearTodayStr);
const adsYesterdayDiff = diffMoneyMark_(adsYesterday - adsLastWeekSame);
const adsThisMonthYoY = diffMoneyMark_(adsThisMonth - adsThisMonthLastYear);
// --- GA4 ---
const gaProps = getGaProperties_();
const gaAllToday = gaAllSites_(gaProps, dToday, dToday);
const gaAllYesterday = gaAllSites_(gaProps, dYesterday, dYesterday);
const gaAllYesterdayLY = gaAllSites_(gaProps, lastYearYesterdayStr, lastYearYesterdayStr);
const gaBySite = gaProps.map(p => {
const today = gaReport_(p.propertyId, dToday, dToday);
const yesterday = gaReport_(p.propertyId, dYesterday, dYesterday);
const yesterdayLY = gaReport_(p.propertyId, lastYearYesterdayStr, lastYearYesterdayStr);
return { label: p.label, today, yesterday, yesterdayLY };
});
const subject = `AdSense / GA デイリーレポート(${dToday})`;
const body = buildMailBody_({
now, tz,
ads: {
today: adsToday,
yesterday: adsYesterday,
yesterdayDiff: adsYesterdayDiff,
thisMonth: adsThisMonth,
thisMonthYoY: adsThisMonthYoY
},
ga: {
all: {
today: gaAllToday,
yesterday: gaAllYesterday,
yesterdayYoYUsers: diffCountMark_(gaAllYesterday.users - gaAllYesterdayLY.users),
yesterdayYoYPv: diffCountMark_(gaAllYesterday.pv - gaAllYesterdayLY.pv)
},
bySite: gaBySite
}
});
MailApp.sendEmail(to, subject, body);
}
function getReportTo_() {
const props = PropertiesService.getScriptProperties();
const to = (props.getProperty('REPORT_TO') || '').trim();
if (to) return to;
return Session.getEffectiveUser().getEmail();
}
/* ========== AdSense v2 ========== */
function adsenseEarnings_(startDate, endDate) {
const accounts = AdSense.Accounts.list();
if (!accounts?.accounts?.length) throw new Error('AdSenseアカウントが見つかりませんでした。');
const accountName = accounts.accounts[0].name;
const res = AdSense.Accounts.Reports.generate(accountName, {
dateRange: 'CUSTOM',
metrics: ['ESTIMATED_EARNINGS'],
...dateToJson_('startDate', parseYmd_(startDate)),
...dateToJson_('endDate', parseYmd_(endDate)),
});
const v = Number(res?.totals?.cells?.[0]?.value ?? 0);
return isFinite(v) ? v : 0;
}
function parseYmd_(yyyyMmDd) {
const [y, m, d] = yyyyMmDd.split('-').map(n => parseInt(n, 10));
return new Date(y, m - 1, d);
}
function dateToJson_(prefix, dateObj) {
return {
[`${prefix}.year`]: dateObj.getFullYear(),
[`${prefix}.month`]: dateObj.getMonth() + 1,
[`${prefix}.day`]: dateObj.getDate(),
};
}
/* ========== GA4(Analytics Data API) ========== */
function gaReport_(propertyId, startDate, endDate) {
const url = `https://analyticsdata.googleapis.com/v1beta/properties/${propertyId}:runReport`;
const payload = {
dateRanges: [{ startDate, endDate }],
metrics: [
{ name: 'activeUsers' },
{ name: 'screenPageViews' }
]
};
const token = ScriptApp.getOAuthToken();
const resp = UrlFetchApp.fetch(url, {
method: 'post',
contentType: 'application/json',
headers: { Authorization: `Bearer ${token}` },
payload: JSON.stringify(payload),
muteHttpExceptions: true
});
const code = resp.getResponseCode();
if (code < 200 || code >= 300) {
throw new Error(`GA runReport error (${code}): ${resp.getContentText()}`);
}
const json = JSON.parse(resp.getContentText());
const row = json?.rows?.[0]?.metricValues || [];
const users = row[0] ? Number(row[0].value) : 0;
const pv = row[1] ? Number(row[1].value) : 0;
return { users: users || 0, pv: pv || 0 };
}
function gaAllSites_(gaProps, startDate, endDate) {
return gaProps.reduce((acc, p) => {
const r = gaReport_(p.propertyId, startDate, endDate);
acc.users += r.users;
acc.pv += r.pv;
return acc;
}, { users: 0, pv: 0 });
}
function getGaProperties_() {
const raw = (PropertiesService.getScriptProperties().getProperty('GA_PROPERTIES') || '').trim();
if (!raw) throw new Error('スクリプトプロパティ GA_PROPERTIES が未設定です。');
const arr = JSON.parse(raw);
if (!Array.isArray(arr) || arr.length === 0) throw new Error('GA_PROPERTIES の形式が不正です。');
return arr;
}
/* ========== Mail body ========== */
function buildMailBody_(ctx) {
const tz = ctx.tz;
const dt = Utilities.formatDate(ctx.now, tz, 'yyyy-MM-dd HH:mm');
const lines = [];
lines.push('AdSense / GA デイリーレポート');
lines.push(dt);
lines.push('');
lines.push('────────────────────────');
lines.push('■ Google AdSense');
lines.push('────────────────────────');
lines.push('');
lines.push('◯ 本日(現時点まで)');
lines.push(`¥${fmtMoney_(ctx.ads.today)}`);
lines.push('');
lines.push('◯ 昨日');
lines.push(`¥${fmtMoney_(ctx.ads.yesterday)}(先週同曜日比:${ctx.ads.yesterdayDiff})`);
lines.push('');
lines.push('◯ 今月(1日〜本日)');
lines.push(`¥${fmtMoney_(ctx.ads.thisMonth)}(前年同期比:${ctx.ads.thisMonthYoY})`);
lines.push('');
lines.push('');
lines.push('────────────────────────');
lines.push('■ Google Analytics(GA4)');
lines.push('────────────────────────');
lines.push('');
lines.push('◯ 全サイト');
lines.push('【本日】');
lines.push(`ユーザー数:${fmtInt_(ctx.ga.all.today.users)} PV数:${fmtInt_(ctx.ga.all.today.pv)}`);
lines.push('');
lines.push('【昨日】');
lines.push(
`ユーザー数:${fmtInt_(ctx.ga.all.yesterday.users)}(前年同日比:${ctx.ga.all.yesterdayYoYUsers}) ` +
`PV数:${fmtInt_(ctx.ga.all.yesterday.pv)}(前年同日比:${ctx.ga.all.yesterdayYoYPv})`
);
lines.push('');
ctx.ga.bySite.forEach(s => {
const yoyUsers = diffCountMark_(s.yesterday.users - s.yesterdayLY.users);
const yoyPv = diffCountMark_(s.yesterday.pv - s.yesterdayLY.pv);
lines.push('');
lines.push(`◯ ${s.label}`);
lines.push('【本日】');
lines.push(`ユーザー数:${fmtInt_(s.today.users)} PV数:${fmtInt_(s.today.pv)}`);
lines.push('');
lines.push('【昨日】');
lines.push(
`ユーザー数:${fmtInt_(s.yesterday.users)}(前年同日比:${yoyUsers}) ` +
`PV数:${fmtInt_(s.yesterday.pv)}(前年同日比:${yoyPv})`
);
});
lines.push('');
lines.push('');
lines.push('────────────────────────');
lines.push('※ ▲ はプラス、▼ はマイナスを示します');
lines.push('※ 本日は現時点までの速報値です');
lines.push('※ 本メールは自動送信されています');
lines.push('────────────────────────');
return lines.join('\n');
}
/* ========== utils ========== */
function formatDate_(date, tz) {
return Utilities.formatDate(date, tz, 'yyyy-MM-dd');
}
function addDays_(date, n) {
const d = new Date(date);
d.setDate(d.getDate() + n);
return d;
}
function fmtMoney_(n) {
const v = Math.round((Number(n) || 0) * 100) / 100;
return v.toLocaleString('ja-JP');
}
function fmtInt_(n) {
return (Number(n) || 0).toLocaleString('ja-JP');
}
function diffMoneyMark_(diff) {
const v = Math.round((Number(diff) || 0) * 100) / 100;
if (v > 0) return `▲+¥${fmtMoney_(v)}`;
if (v < 0) return `▼¥${fmtMoney_(Math.abs(v))}`;
return '±¥0';
}
function diffCountMark_(diff) {
const v = Number(diff) || 0;
if (v > 0) return `▲+${fmtInt_(v)}`;
if (v < 0) return `▼${fmtInt_(Math.abs(v))}`;
return '±0';
}
コードを書いたら、sendDailyReportを実行してメールが届くかを確認。
9. トリガー設定(7時/18時)
Apps Script の左メニューの 「トリガー」から設定します。
- トリガーを追加
- 実行する関数:sendDailyReport
- イベントのソース:時間主導型
- 日付ベースのタイマー
- 時刻:午前7時〜8時
- 同じくもう1つ追加
- 時刻:午後6時〜7時
GASは分散実行なので、時間ぴったりには保証されませんが、日次レポ用途なら問題になりにくいです。
時間はお好みで。
実行回数を増やすも良し、朝一回だけとかでもありですね。
おわりに
自分からアプローチするのではなく、自動で数字が届くと、意外と行動が早くなるんですよね。
昨日から伸びてるから記事を追記しよう、とか。
アドセンスは主にブロガーの方なんかにおすすめかな、と。
企業ではアドセンスはいらないことが多いかと思うので、アドセンス部分を外して運用するのもありかと思われます。
たかがブラウザ開いて確認するだけのことではありますが、日々の数秒も年間で換算すれば……ってやつで。
毎日見ていた数字を、朝夕のメールにしておくだけで「見に行く手間」がなくなって非常に快適なのです。
