🌿 えふてぃブログ

【実体験】GA4 APIのRESOURCE_EXHAUSTEDは「待つだけ」で直った


📑 目次

こんにちは、えふてぃです。

「ついさっきまでは普通にビルドが通っていたのに、なぜかPV数が取得できなくなってしまった…」

GA4 Data APIを使ってブログにPV数を表示している方には唐突なホラーかもしれませんが、
実は先日、まさにこの状況に遭遇しました。

原因を調べていくと、どうやら「サーバーエラークォータ」という制限に引っかかっていたことが分かりました。

この記事では、実際に体験したトラブルの経緯と、GA4 Data API 無料枠のクォータ制限の全体像、そして対策を備忘録としてまとめます。
同じエラーに遭遇して困っている方の参考になれば嬉しいです〜。

環境

  • Astro: 5.15.3
  • Node.js: 22.14.0
  • @google-analytics/data: 5.2.1
  • OS: macOS

本記事は上記の環境で検証しています。

突然ビルドが通らなくなった

いつも通り npm run build を実行したら、こんなエラーが出ました。

❌ GA4 Data API エラー: データ取得に失敗しました
詳細: 8 RESOURCE_EXHAUSTED: Exhausted server errors quota.
This quota tokens will return in under an hour.

ビルド自体は完走するのですが、GA4からのデータ取得が全滅。
PV数が全て0になってしまう状態です。

なんだこれは???最近ブログをサボっていた天罰か???
サボりすぎてPV数すら没収されるほどの状態になってしまったのか…。

悲しい気持ちになりつつとりあえずはざっと構成を振り返りました。

ざっとしたブログのGA4 API構成をご紹介

このブログでは、ビルド時にGA4 Data API v1を使ってPVデータを取得しています。

具体的には、astro.config.mjs にViteプラグインとして仮想モジュールを定義して、ビルド開始時に @google-analytics/dataBetaAnalyticsDataClient で3つのAPIリクエストを Promise.all で並列実行しています。

  1. 記事別PV数 — 各記事カードに閲覧数を表示するため
  2. 総PV数 — サイト全体のPV数を表示するため
  3. 人気記事ランキング — サイドバーの人気記事表示用

取得したデータは仮想モジュール(virtual:ga4-data)にキャッシュされて、各Astroコンポーネントから import して使い回す仕組みです。
つまり、1回のビルドでAPIリクエストは3回だけ。何度もAPIを叩くような構成ではないので相当分のビルドをぶん回してもリクエスト上限には引っかからないはずなのに…。

最初に疑ったこと

「何か壊した?」と思って、まず確認したのはこのあたりです。

  • コードを変更した? → していない
  • 環境変数が消えた?.env を確認したが問題なし
  • パッケージを更新した? → していない

どれも該当しませんでした。

そこで、改めてエラーメッセージをよく読んでみました。

RESOURCE_EXHAUSTED: Exhausted server errors quota.

「server errors quota」…?

自分のリクエスト回数が多すぎるのではなく、サーバーエラーのクォータが枯渇しているということのようです。
これは調べてみる必要がありそうだと思い、公式ドキュメントを確認してみました。

GA4 Data API 無料枠のクォータ制限を調べてみた

Google公式のクォータドキュメントを読んでみたら、思っていたよりも多くの制限があることが分かりました。

クォータ一覧(無料 vs 有料)

クォータ種別無料(Standard)有料(Analytics 360)リセット
トークン / プロパティ / 日200,0002,000,000毎日 午前0時(PST)
トークン / プロパティ / 時40,000400,0001時間以内
トークン / プロジェクト / プロパティ / 時14,000140,0001時間以内
同時リクエスト数 / プロパティ1010
サーバーエラー / プロジェクト / プロパティ / 時10101時間以内

有料版(Analytics 360)はトークン系が10倍ですが、注目すべきはサーバーエラークォータは有料でも同じ10回という点です。

トークン消費量の実感

「トークン」と聞くとAI系のトークンを想像してしまいますが、GA4 APIではリクエストの複雑さに応じて消費されるポイントのようなものです。

公式ドキュメントによると、ほとんどのリクエストは10トークン以下で済みます。

私のブログの場合だと、こんな計算になります。

  • 1ビルド = 3リクエスト × 約10トークン = 約30トークン
  • 1日の上限 = 200,000トークン
  • つまり、1日に約6,600回ビルドしても大丈夫な計算

トークン数だけ見れば、個人ブログで枯渇することはまずありません。

👤

じゃあなんでエラーになったの?

トークンの問題じゃなくて、「サーバーエラークォータ」が原因でした。

🤖

落とし穴:サーバーエラークォータ(1時間にたった10回)

ここが今回のエラー原因です。

GA4 Data APIには、Google側でサーバーエラー(500/503)が発生した回数をカウントするクォータがあります。
これが「サーバーエラークォータ」です。

仕組みはこうです。

  1. APIリクエストを送る
  2. Google側の問題で500や503エラーが返される
  3. この失敗が「サーバーエラー」としてカウントされる
  4. 1時間に10回カウントされると、以降のリクエストは全てブロックされる
📝 ポイント

サーバーエラークォータは、自分のリクエスト回数とは関係ありません。Google側が一時的に不安定だった時に、たまたまリクエストが重なるとカウントが溜まります。

そして厄介なのが、クォータが枯渇した後の挙動です。
正常なリクエストも含めて全てブロックされます。Google側のサーバーが回復していても、クォータが回復するまではリクエストが通りません。

なぜ今回引っかかったのか

振り返ってみると、原因はClaude Codeで色々な開発を半自動的に回していたことでした。

私は普段、Claude Code(AIコーディングツール)を使って機能追加やリファクタリングなどの開発作業を進めています。
その過程で、動作確認のために npm run build を何も考えずにぽちぽち承認してました。

  1. Google側のサーバーがたまたま不安定だった
  2. Claude Codeで複数の開発タスクを並行して進めていた
  3. タスクごとにビルド確認 → その都度GA4 APIが叩かれる
  4. Google側のエラーが返るたびにサーバーエラークォータが消費される
  5. 気づかないうちに10回に到達 → 完全にブロック

手動で何回もリトライしたわけではなく、開発フローの中で自然とビルドが積み重なっていたのがポイントです。
1ビルド3リクエストなので、ビルドが3〜4回走るだけでサーバーエラークォータの上限に届いてしまいます。

ビルドのたびにAPIを叩くと、開発のテンポが上がるほどクォータを消費しやすくなり良くないですね
(⬆️そもそもbuildしなくて良いとこしてたのもありちゃんと整備しろという話)

RESOURCE_EXHAUSTEDエラーの見分け方と対処法

エラーメッセージの読み方

RESOURCE_EXHAUSTED エラーには実はいくつかの種類があります。
エラーメッセージを見れば、どのクォータに引っかかっているのか判断できます。

エラーメッセージ原因対処法
Exhausted tokens quotaトークン消費量の上限に到達リクエストの最適化 or 時間をおいて待機
Exhausted server errors quotaGoogle側のサーバーエラーが累積1時間待つ(リトライ厳禁)
Exhausted concurrent requests quota同時リクエスト数の超過並列リクエスト数を減らす

今回引っかかったのは2番目の「server errors quota」です。
「tokens」なのか「server errors」なのかで対処法がまったく違うので、エラーメッセージはしっかり読みましょう(自戒を込めて)。

対策1:エラー時のリトライを我慢する

一番大切なのは、焦ってリトライしないことです。

エラーメッセージに This quota tokens will return in under an hour. と書いてある通り、1時間待てば自動的に回復します。

❌ ビルド失敗 → すぐ再ビルド → また失敗 → また再ビルド → 完全にブロック
✅ ビルド失敗 → エラーログ確認 → RESOURCE_EXHAUSTED → 1時間待つ → 回復

「待つだけ」というのはもどかしいですが、これが最善の対処法です。

対策2:ローカルキャッシュでフォールバックする

「1時間も待てない」「ビルドを止めたくない」という場合は、前回取得したデータをローカルにキャッシュしておいて、API失敗時にフォールバックする仕組みが有効です。

考え方としてはこんなイメージです。

// API成功時 → データをJSONファイルに保存
// API失敗時 → 保存済みのJSONファイルから読み込み

async function fetchGA4DataWithFallback() {
  try {
    const data = await fetchFromGA4API();
    // 成功したらキャッシュに保存
    await saveCache(data);
    return data;
  } catch (error) {
    console.warn('GA4 API失敗、キャッシュからフォールバックします');
    return await loadCache();
  }
}

PV数は数時間で劇的に変わるものでもないので、前回のデータで代用しても実用上はほぼ問題ありません。

対策3:ビルド頻度を意識する

CI/CDで自動ビルドをしている場合は、ビルド頻度にも注意が必要です。

私のブログの場合、1ビルドで3リクエストなので、トークン数は余裕があります。
ただし、Google側が不安定な時間帯にCIが何度もビルドを走らせると、サーバーエラーが累積するリスクがあります。

  • CIのリトライ回数を制限する
  • ビルド間隔を適度に空ける
  • GA4データ取得の失敗でビルド全体を落とさない設計にする

こうした工夫をしておくと、安心してCI/CDを回せます。

まとめ:無料枠でも十分使えるが、エラー時の振る舞いは知っておくべき

今回の体験で学んだことをまとめます。

  • GA4 Data API 無料枠のトークン数は十分すぎるほどある(日に200,000トークン)
  • ただしサーバーエラークォータは1時間に10回と少なく、ここが落とし穴
  • RESOURCE_EXHAUSTED エラーが出たら、まずエラーメッセージを読んで種類を見分ける
  • 「server errors quota」の場合はリトライ厳禁、1時間待てば回復する
  • フォールバック用のローカルキャッシュを用意しておくとさらに安心

個人ブログでGA4 Data APIを使う分には、無料枠で全く問題ありません。
ただ、今回のようにGoogle側の一時的な不調 × リトライの繰り返しで引っかかることがあるので、「サーバーエラークォータ10回/時」という数字だけは覚えておくと良いですよ。

同じエラーで困っている方の参考になれば嬉しいです。

参考リンク:


このブログが面白いと思った方は、是非とも下のボタンをクリックして、記事シェアやフォローをしていただけると嬉しいです!