アドセンスとアナリティクスのレポートをGASで日々メール送信してみた(AdSense + GA4)

アドセンスとアナリティクスのレポートをGASで日々メール送信してみた(AdSense + GA4) 技術メモ

本ページはアフィリエイトプログラムを利用しています

毎朝、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)


────────────────────────
※ ▲ はプラス、▼ はマイナスを示します
※ 本日は現時点までの速報値です
※ 本メールは自動送信されています
────────────────────────

こんなメールが飛んできます。

ざっくり全体構成

  1. GCPプロジェクトを作る
  2. OAuth同意画面を設定する
  3. APIを有効化する
    • AdSense Management API
    • Google Analytics Data API(GA4)
  4. Apps Script を GCP に紐付ける
  5. appsscript.json にスコープを追加する
  6. スクリプトプロパティを設定(送信先・GAプロパティ一覧)
  7. コードを貼る → 実行 → 承認
  8. トリガーを設定

実装方法

1. GCPプロジェクト作成(Cloud Console)

アドセンスとアナリティクスのレポートをGASで日々メール送信してみた(AdSense + GA4)
Google Cloud Platform
Google Cloud Platform lets you build, deploy, and scale applications, websites, and services on the same infrastructure ...

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は分散実行なので、時間ぴったりには保証されませんが、日次レポ用途なら問題になりにくいです。

時間はお好みで。
実行回数を増やすも良し、朝一回だけとかでもありですね。

おわりに

自分からアプローチするのではなく、自動で数字が届くと、意外と行動が早くなるんですよね。
昨日から伸びてるから記事を追記しよう、とか。

アドセンスは主にブロガーの方なんかにおすすめかな、と。
企業ではアドセンスはいらないことが多いかと思うので、アドセンス部分を外して運用するのもありかと思われます。

たかがブラウザ開いて確認するだけのことではありますが、日々の数秒も年間で換算すれば……ってやつで。
毎日見ていた数字を、朝夕のメールにしておくだけで「見に行く手間」がなくなって非常に快適なのです。

タイトルとURLをコピーしました