この記事でわかること
- Meta広告とYahoo!広告のBigQuery連携には、APIでデータを取得して自作スクリプトで書き込む実装が必要です。
- Google Apps Scriptなら無料で実装でき、日次バッチなら6分以内の実行時間制限で十分対応できます。
- トークン失効やAPI廃止に備えて、失敗検知の通知と定期的な更新を運用に組み込む必要があります。
生成AIによる要約
なぜMeta広告とYahoo!広告は実装が必要なのか
Google広告にはBigQuery Data Transfer Service、GA4にはBigQueryエクスポートという公式連携があります。一方、Meta広告とYahoo!広告には、BigQueryへ直接データを送る公式コネクタがありません。この2媒体を分析基盤に加えるには、APIでデータを取得して自分で書き込む実装が必要になります。
| 媒体 | BigQueryへの連携手段 | 実装の要否 |
|---|---|---|
| Google広告 | Data Transfer Service(公式) | 設定のみ |
| GA4 | BigQueryエクスポート(公式) | 設定のみ |
| Meta広告 | Marketing API+自作スクリプト | 実装が必要 |
| Yahoo!広告 | LINEヤフー広告 API+自作スクリプト | 実装が必要 |
実装と聞くと身構えるかもしれませんが、日次でレポートデータを取り込むだけなら、Google Apps Script(GAS)で完結します。サーバーの契約も追加費用も不要です。筆者も複数の広告アカウントでこの構成を本番運用しており、一度構築すれば手を入れる場面はほとんどありません。
この記事はBigQueryの基本を理解している前提で進めます。初めての場合はBigQueryで広告データを集約する方法から読むことをおすすめします。Google広告の連携はData Transfer Serviceの設定ガイドをご覧ください。
全体アーキテクチャ
構成はシンプルです。GASが時間トリガーで毎日起動し、各媒体のAPIから前日分のレポートを取得して、BigQueryにロードします。
実装環境はGASのほかに、Cloud RunやCloud Functionsを使う選択肢もあります。判断基準は次のとおりです。
| 観点 | GAS | Cloud Run+Scheduler |
|---|---|---|
| 追加費用 | なし | 無料枠超過分が発生しうる |
| 実行時間の制限 | 1回6分まで | 実質制約なし |
| 開発・保守の負荷 | 低い | 環境構築とデプロイ管理が必要 |
| 向いているケース | 日次バッチ・少数アカウント | 大量アカウント・高頻度実行 |
GASの実行時間は公式の割り当てで1回6分までです。日次のキャンペーン単位レポートであれば、経験上この制限内に十分収まります。まずGASで作り、限界が見えたらCloud Runへ移す順番が現実的です。GASの基礎はGASで広告運用を自動化するで解説しています。
Meta広告のデータを取得する
準備: アプリとシステムユーザートークン
Meta広告のデータ取得には、Meta for Developersでのアプリ作成と、アクセストークンの発行が必要です。ここで重要なのがトークンの種類の選択です。
通常の手順で発行される長期ユーザートークンは、約60日で失効します。自動連携が2か月ごとに止まる原因になるため、バッチ用途には向きません。バッチ用途には、ビジネスマネージャの「システムユーザー」で発行するトークンを使います。
システムユーザートークンは、発行時に有効期限を「無期限」に設定できる場合があります。ただし、この選択肢が常に出るとは限りません。アプリのアクセスレベルやビジネス認証の状態によっては、60日のトークンしか発行できないことがあります。筆者が関わった3つのビジネスマネージャでは、いずれも無期限を選べませんでした。
「システムユーザートークンなら失効しない」と考えて設計すると、2か月後に取り込みが静かに止まります。発行したトークンの種別と期限は、debug_token エンドポイントで確認できます。期限が付いていた場合は、次の交換の仕組みを必ず組み込んでください。
なお、システムユーザートークンも権限変更などのイベントで無効になることがあります。完全に「設定して終わり」にはならない前提で、失敗検知の通知を用意しておきましょう。
60日トークンは期限内に交換し続ける
有効期限のあるトークンは、fb_exchange_token で新しいトークンに交換できます。交換した時点から60日を取り直せるため、期限が来る前に定期実行しておけば失効しません。GASなら週1回の時間トリガーで足ります。
重要なのは、期限が切れたトークンは交換できないという点です。交換の仕組み自体が止まっていることに気づかないまま期限を過ぎると、手動での再発行に戻ります。週1回で回していれば、7回続けて失敗しない限り猶予があります。
function refreshMetaToken() {
const token = PROPS.getProperty("META_ACCESS_TOKEN");
const url = `https://graph.facebook.com/${META_API_VERSION}/oauth/access_token`
+ "?grant_type=fb_exchange_token"
+ `&client_id=${PROPS.getProperty("META_APP_ID")}`
+ `&client_secret=${PROPS.getProperty("META_APP_SECRET")}`
+ "&set_token_expires_in_60_days=true"
+ `&fb_exchange_token=${encodeURIComponent(token)}`;
const res = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
const body = JSON.parse(res.getContentText());
if (res.getResponseCode() !== 200 || !body.access_token) {
// 交換に失敗したら即座に気づける状態にする。期限切れ後は再発行しか手がない
MailApp.sendEmail(PROPS.getProperty("NOTIFICATION_EMAIL"),
"[Meta] トークンの交換に失敗", res.getContentText());
throw new Error("token refresh failed");
}
PROPS.setProperty("META_ACCESS_TOKEN", body.access_token);
}
set_token_expires_in_60_days=true は付けておきます。これがないと、交換しても期限が元のまま返ることがあります。交換のあとは debug_token で期限が延びたことを確認してください。
insightsエンドポイントの基本
レポートデータは /act_{広告アカウントID}/insights エンドポイントから取得します。主要パラメータは次の4つです。
| パラメータ | 役割 | 設定例 |
|---|---|---|
level | 集計粒度 | campaign(ほかに account / adset / ad) |
fields | 取得する指標 | spend,impressions,clicks,actions など |
time_range | 対象期間 | {"since":"2026-07-01","until":"2026-07-23"} |
time_increment | 期間の分割 | 1 で日別(既定は期間合計) |
コンバージョンは actions フィールドに種類別の配列で返ります。さらに action_attribution_windows パラメータで 7d_click や 1d_view を指定すると、クリック起因とビュー起因の数値を別々のキーで取得できます。Meta広告の申告コンバージョンはビュースルーの比率が高いことも多く、この分離は分析の質を大きく左右します。最初から分けて蓄積しておくことを強くおすすめします。
GASでの取得コード
キャンペーン単位・日別でインサイトを取得する実装例です。トークンやアカウントIDは、流出防止のためスクリプトプロパティに保存して参照します。
const PROPS = PropertiesService.getScriptProperties();
const META_API_VERSION = "v25.0";
function fetchMetaInsights(sinceDate, untilDate) {
const accountId = PROPS.getProperty("META_ACCOUNT_ID");
const token = PROPS.getProperty("META_ACCESS_TOKEN");
const params = {
level: "campaign",
fields: "campaign_id,campaign_name,spend,impressions,clicks,actions",
time_range: JSON.stringify({ since: sinceDate, until: untilDate }),
time_increment: "1",
action_attribution_windows: JSON.stringify(["7d_click", "1d_view"]),
limit: "500",
access_token: token,
};
const query = Object.keys(params)
.map(function (k) { return k + "=" + encodeURIComponent(params[k]); })
.join("&");
let url = "https://graph.facebook.com/" + META_API_VERSION +
"/act_" + accountId + "/insights?" + query;
const rows = [];
while (url) {
const res = JSON.parse(UrlFetchApp.fetch(url).getContentText());
(res.data || []).forEach(function (r) { rows.push(flattenMetaRow(r)); });
url = res.paging && res.paging.next ? res.paging.next : null;
}
return rows;
}
function flattenMetaRow(row) {
let cvClick = 0;
let cvView = 0;
(row.actions || []).forEach(function (a) {
if (a.action_type === "offsite_conversion.fb_pixel_purchase") {
cvClick += Number(a["7d_click"] || 0);
cvView += Number(a["1d_view"] || 0);
}
});
return {
date: row.date_start,
media: "meta",
campaign_id: row.campaign_id,
campaign_name: row.campaign_name,
cost: Number(row.spend || 0),
impressions: Number(row.impressions || 0),
clicks: Number(row.clicks || 0),
cv_click: cvClick,
cv_view: cvView,
};
}
flattenMetaRow で集計対象にする action_type は、アカウントの計測設計によって変わります。購入なら offsite_conversion.fb_pixel_purchase、リード獲得なら別のタイプになるため、まず一度APIの生レスポンスを確認し、自社のCVに対応するタイプを特定してください。
レート制限は広告アカウント単位で管理され、アプリのアクセスレベルによって上限が大きく異なります。日次バッチで数アカウントを扱う範囲なら、通常は問題になりません。
Yahoo!広告のデータを取得する
準備: API利用の申請
Yahoo!広告のAPI(現名称はLINEヤフー広告 API)は、誰でもすぐ使えるわけではありません。Yahoo! JAPANビジネスIDでのログインと、API管理ツールでのアプリケーション登録の手続きが必要です。検索広告とディスプレイ広告でAPIの体系が分かれている点も、最初に押さえておきましょう。
認証はOAuth 2.0で、発行したリフレッシュトークンからアクセストークンを更新しながら利用します。Metaのシステムユーザートークンとは管理の考え方が異なるため、トークン更新処理を実装に含める必要があります。
非同期レポートの3ステップ
Yahoo!広告のレポート取得は、Metaのような同期型ではなく非同期型です。レポートの定義を登録し、生成完了を待ってからダウンロードする流れになります。
リクエストの具体的な項目定義はAPIバージョンごとに変わるため、実装時は必ず公式リファレンスの現行バージョンの定義に沿って組み立ててください。実装のポイントは次の3点です。
- レポート定義には日別・キャンペーン別の粒度と、費用・表示回数・クリック・CVの項目を含める
- ポーリングは数秒間隔にとどめ、GASの6分制限内に完了しない場合は取得期間を分割する
- ダウンロードしたCSVはヘッダー行と合計行の扱いに注意して整形する
なお、Yahoo!広告は検索広告とディスプレイ広告でレポートAPIが別々です。両方を配信している場合は、それぞれに同じ3ステップを実装し、整形後に共通スキーマへそろえます。
BigQueryへロードする
共通スキーマの設計
媒体ごとにバラバラの形式で保存すると、後の横断分析が面倒になります。取り込み時点で共通スキーマにそろえるのが定石です。最小構成として、次の項目をおすすめします。
| 列名 | 型 | 内容 |
|---|---|---|
date | DATE | 配信日(パーティション列にする) |
media | STRING | 媒体名(meta / yahoo_search など) |
campaign_id | STRING | キャンペーンID |
campaign_name | STRING | キャンペーン名 |
cost | FLOAT64 | 費用 |
impressions | INT64 | 表示回数 |
clicks | INT64 | クリック数 |
cv_click | FLOAT64 | クリック起因CV |
cv_view | FLOAT64 | ビュー起因CV |
date 列でパーティション分割しておくと、クエリのスキャン量と費用を抑えられます。テーブル設計の考え方は広告データ集約の基礎も参照してください。
GASからのロード実装
GASの「高度なGoogleサービス」でBigQueryを有効化すると、ロードジョブを直接投入できます。整形済みの行データをNDJSON形式にしてロードする実装例です。
function loadToBigQuery(rows, targetDate) {
if (!rows.length) return;
const projectId = PROPS.getProperty("BQ_PROJECT_ID");
const datasetId = "ads";
const tableId = "daily_campaign_stats";
const ndjson = rows.map(function (r) { return JSON.stringify(r); }).join("\n");
const blob = Utilities.newBlob(ndjson, "application/octet-stream");
const job = {
configuration: {
load: {
destinationTable: {
projectId: projectId,
datasetId: datasetId,
tableId: tableId,
},
sourceFormat: "NEWLINE_DELIMITED_JSON",
writeDisposition: "WRITE_APPEND",
timePartitioning: { type: "DAY", field: "date" },
autodetect: true,
},
},
};
const inserted = BigQuery.Jobs.insert(job, projectId, blob);
Logger.log("load job: " + inserted.jobReference.jobId);
}
ロードジョブ自体は無料で、課金対象はストレージとクエリだけです。ストリーミング挿入(insertAll)は料金が発生するうえ日次バッチには不要なので、ロードジョブ方式を選んでください。
洗い替え設計: 数値の遡及更新に備える
この構成で最も重要な設計判断が「何日分を取り直すか」です。Meta広告のコンバージョンはアトリビューションウィンドウの期間内、数値が後から増えていきます。前日分を1回取り込んで終わりにすると、BigQueryの数値だけが古いまま管理画面とずれ続けます。
実装は「直近7日分をDELETEしてから、同じ7日分を取得してAPPENDする」だけです。日次実行の冒頭に次のクエリを挟みます。
function deleteRecentRows() {
const projectId = PROPS.getProperty("BQ_PROJECT_ID");
const sql =
"DELETE FROM `" + projectId + ".ads.daily_campaign_stats` " +
"WHERE media = 'meta' " +
"AND date >= DATE_SUB(CURRENT_DATE('Asia/Tokyo'), INTERVAL 7 DAY)";
const request = { query: sql, useLegacySql: false };
BigQuery.Jobs.query(request, projectId);
}
取り直す日数は、アカウントのアトリビューション設定に合わせます。7日クリックを使っているなら7日分、より長いウィンドウなら日数を延ばしてください。Yahoo!広告も同じ洗い替えに含めておくと、媒体側の数値修正に自動で追随できます。
運用の注意点
構築後に実際へ起きやすいトラブルを挙げます。いずれも筆者が本番運用で経験したものです。
| リスク | 対策 |
|---|---|
| トークンの失効・無効化 | 期限を控えて交換処理を定期実行し、取得失敗時はSlack等へ通知する |
| APIバージョンの廃止 | Metaは約2年でバージョンが失効するため、年1回の更新を予定に入れる |
| 実行の無音失敗 | 日次で行数をチェックし、0件の日はアラートを出す |
| 数値の定義ずれ | 導入時に管理画面と数日分を突き合わせ、CV定義を確定させる |
特に無音失敗への備えは重要です。トリガーは動いているのにデータが入っていない、という状態は気づきにくく、欠損期間が長いほど復旧に手間がかかります。「昨日の日付の行が存在するか」を確認するだけの監視クエリでも、あるとないとでは大違いです。
見落としやすいのが、下流のレポート側の挙動です。媒体別に集計するレポートは、行が無い媒体を黙って除外してしまうことがあります。数字が小さくなるのではなく媒体ごと消えるため、レポートを見ても異常だと気づけません。媒体の最終着弾日を確認し、遅れていれば警告を出す作りにしておくと安全です。
蓄積したデータの活用はBigQueryのSQL実践ガイドや、AIエージェントとのMCP接続に進むと広がります。日本語で媒体横断の分析ができる環境は、この取り込み基盤があってこそ成立します。
まとめ
Meta広告・Yahoo!広告のBigQuery取り込みについて、実装の要点を整理しました。
| 工程 | 要点 |
|---|---|
| 方式選定 | まずGAS。制限に当たったらCloud Runへ |
| Meta取得 | システムユーザートークン+insights、CVはクリック/ビューを分離 |
| Yahoo!取得 | API利用申請のうえ、非同期レポートの3ステップで実装 |
| ロード | 共通スキーマ+日付パーティション+ロードジョブ |
| 洗い替え | 直近7日を毎日削除→再取得で遡及更新に追随 |
| 運用 | トークンの交換、失敗通知、行数監視を最初から組み込む |
公式コネクタがない媒体の連携は、最初の構築にこそ手間がかかりますが、動き始めれば日々のレポート業務を大きく減らしてくれます。まずはMeta広告1アカウントから、管理画面との数値突き合わせまでを一巡させてみてください。
関連記事
この記事をシェア
参照元
ryottaman
広告代理店で17年、現在は事業会社でインハウスマーケティングを担当しています。
SIGNALZの記事はAIを活用して作成し、10年以上の運用型広告の実務経験をもとに内容を確認・監修しています。制作方針の詳細はサイトについてをご覧ください。
Google検索の優先ソースにSIGNALZを追加
追加すると、Google検索のトップニュースでSIGNALZの記事が見つけやすくなります。AIモードとAIによる概要でも「優先」バッジが付きます。設定はいつでも解除できます。