複数の広告プラットフォームからデータを取得するレポーティング基盤を、社内で保守しているエンジニアに向けた内容です。断続的なレート制限エラーに悩まされている場合、実は自分のコードの不具合ではなく、プラットフォーム側の仕様どおりの挙動という可能性があります。判断のヒントになれば幸いです。
Google広告とMeta広告からそれぞれ支出・クリック数・コンバージョン数を取得し、ダッシュボードに表示する。要件だけを見れば単純な集計処理に見えます。ところが本番運用でクライアント数が増えていくと、断続的に429(Too Many Requests、レート制限超過を示すHTTPステータスコード)が返るようになるケースが報告されています。しかも常に発生するわけではなく、間欠的に起きるため原因の切り分けが難しくなります。
何が起きているのか:2つのAPIで全く違うレート制限の仕組み
Google Ads APIは開発者トークン単位でオペレーション枠(quota、割り当て量)が決まっています。しかもアカウントの階層(tier)によって枠の大きさが変わる仕組みです。一方でMeta Marketing APIは、アプリ単位ではなく広告アカウント単位の累積利用スコア(rolling usage score)でスロットリング(通信量の絞り込み)を判断します。
ここが混乱の元になります。単一クライアントのAPI呼び出し回数を見ても正常なのに、複数クライアントの呼び出しを合算するとMeta側の閾値を超えている、という状態が起こり得るからです。個々のアカウントは全く問題ないのに、アプリ全体の累積量が引き金になって429が返る構造です。
3クライアント程度の運用では問題が顕在化しなくても、10社を超えたあたりから急に429が混ざり始めるという事例が報告されています。原因をリトライロジックやジョブスケジューラの競合状態(race condition)だと疑って、コード側を何週間も調査しても解決しないのは、そもそも原因がコードではなくアプリ全体のAPI呼び出し総量にあるためです。
段階的に見ていく:多社連携パイプラインに必要な構成要素
1. cronではなくキューで制御する
定期実行のcronジョブでAPIを一斉に叩く設計は、クライアント数が増えるほど破綻しやすくなります。BullMQ(Node.js向けのジョブキューライブラリ)やSidekiq(Ruby向けの非同期ジョブ処理ライブラリ)のような、リトライポリシーと指数バックオフ(exponential backoff、失敗のたびに待機時間を段階的に伸ばす再試行方式)を最初から組み込めるキューを使う設計が推奨されています。
さらに、稼働中のダッシュボードを優先して更新し、閲覧されていないアカウントは後回しにする優先度制御を組み合わせることで、限られたAPI枠を有効に配分できます。
2. トークンリフレッシュを独立した監視対象にする
OAuth(外部サービスへの認証・認可を行う標準プロトコル)のトークンは、通知なく静かに期限切れになります。一般的なエラーログと同じ扱いにしていると、クライアントから「ダッシュボードが空です」と問い合わせが来て初めて気づく、という事態になりかねません。認証失敗は専用の監視項目として分離しておく判断が有効です。
3. 生データと保存層の間に正規化レイヤーを置く
Google Adsは「cost」、Metaは「spend」、GA4(Google Analytics 4)はまた別の次元名で同じ指標を表現します。フロントエンド側にプラットフォーム固有のフィールド名を処理させるのではなく、内部で統一スキーマを1つ定義し、各プラットフォームのレスポンスをそこにマッピングする設計が保守性を高めます。
// 正規化処理の簡略例
function normalizeMetric(platform, rawData) {
const mapping = {
google_ads: { cost: rawData.cost_micros / 1e6, clicks: rawData.clicks },
meta: { cost: rawData.spend, clicks: rawData.clicks },
};
return mapping[platform] || rawData;
}この関数自体は単純に見えますが、実運用では通貨換算・タイムゾーンのずれ・アトリビューションウィンドウ(コンバージョンの貢献をどの期間まで遡って計上するかの設定)の違いが複数プラットフォームで同時に絡み合うため、想定より複雑になりやすい部分です。
背景:なぜこの手の問題は見積もりを外しやすいのか
エンタープライズの現場で外部SaaS(Software as a Service)連携を見積もる際、API仕様書に書かれた「レート制限:1時間あたりN回」という数字だけを見て工数を積むと、今回のような累積スコア型の制限を見落としがちです。とくにMeta Marketing APIのようにアプリ単位でスコアが合算される方式は、単体アカウントでの検証だけでは再現しません。
日本の現場では、社内向けBIツールやマーケティングダッシュボードを内製している企業も少なくありません。GoogleスプレッドシートやLooker Studioへの連携を自前のバッチで組んでいる場合も、同じ構造の落とし穴が存在します。クライアント数・連携先数が増える計画があるなら、初期設計の段階でキュー方式を前提にしておく方が、後からの作り直しコストを避けやすくなります。
今日確認できること
自分のプロジェクトが該当するか、次の観点で確認してみてください。
| 確認項目 | 確認する場所 | 見るポイント |
|---|---|---|
| Google Ads APIの枠 | Google Ads APIコンソールの開発者トークン設定 | アカウント階層ごとのオペレーション上限 |
| Metaのレート状況 | Graph APIレスポンスのX-App-Usageヘッダー | アプリ単位の累積使用率(%表示) |
| 429発生タイミング | APIコール量のログとエラーログの突合 | 個別アカウントか累積量かの切り分け |
| OAuthトークン | 認証エラーの専用ログ・アラート設定 | 一般エラーログと分離されているか |
内製を続けるか、外部の専門プラットフォームに委譲するかの判断基準も整理しておくと良さそうです。レポーティング基盤が自社プロダクトの差別化要素そのものでないなら、レート制限対応・トークン監視・正規化層の保守に割く工数を、他の機能開発に振り向けられないか検討する余地があります。逆に、社内CRMや独自データウェアハウスなど、外部に存在しない情報源との連携が中心なら、内製を続ける合理性は残ります。
まとめ
多社の広告APIを連携するレポーティング基盤では、429エラーの原因をコードのバグと決めつける前に、プラットフォームごとのレート制限方式の違いを確認する価値があります。
- Google Ads APIは開発者トークン単位、Metaはアプリ単位の累積スコアという別方式であることを把握する
- cronによる一斉呼び出しをやめ、指数バックオフ付きのキュー(BullMQ・Sidekiqなど)と優先度制御を導入する
- OAuthトークンの期限切れは専用の監視項目として分離する
- 生データと保存層の間に正規化レイヤーを置き、プラットフォームごとの命名差異を吸収する
まずはMeta Graph APIのレスポンスに含まれるX-App-Usageヘッダーを確認し、アプリ単位の累積使用率が閾値に近づいていないか、直近のログから見直してみるところから始められます。