Google Apps Scriptの「Service invoked too many times: urlfetch」エラーを解決する方法

intermediate📗 Google Sheets2026-04-08| Google Apps Script (V8 Engine), Google Sheets, Google Workspace アカウント。

Error Message

Exception: Service invoked too many times for one day: urlfetch.
#google-apps-script#urlfetch#quota#api-limit#optimization

問題の概要

このエラーが発生すると、まるで壁にぶつかったような感覚に陥ります。これは、スクリプトが1日の UrlFetchApp.fetch() 呼び出し制限を使い果たしたことを意味します。Googleはこれらのリクエストを24時間のスライディングウィンドウで追跡しています。個人の @gmail.com アカウントの場合、制限は1日あたり20,000回です。Google Workspace ユーザーは、100,000回というより大きな枠を利用できます。

エラーは通常、実行ログに次のように表示されます。

Exception: Service invoked too many times for one day: urlfetch.

これは通常、スプレッドシートの何百もの行を処理するループ内でスクリプトが実行された場合に発生します。また、=GET_DATA() のようなカスタム関数が何百ものセルに配置され、シートの再計算のたびに実行される場合にもよく見られます。

ステップ1:CacheServiceによるキャッシュの実装

同じデータを二度要求しないでください。株価や天気予報のように、毎分変わるわけではない情報を取得する場合は、CacheService に保存しましょう。標準的なAPI呼び出しには500ミリ秒ほどかかることがありますが、キャッシュからの読み取りは20ミリ秒未満で済みます。

修正前(非効率)

function getPrice(ticker) {
  const response = UrlFetchApp.fetch('https://api.example.com/price/' + ticker);
  return JSON.parse(response.getContentText()).price;
}

修正後(キャッシュ利用)

function getPriceWithCache(ticker) {
  const cache = CacheService.getScriptCache();
  const cachedValue = cache.get(ticker);
  
  if (cachedValue != null) return cachedValue;

  const response = UrlFetchApp.fetch('https://api.example.com/price/' + ticker);
  const price = JSON.parse(response.getContentText()).price;
  
  // 1時間(3600秒)保存する
  cache.put(ticker, price.toString(), 3600);
  return price;
}

ステップ2:リクエストのバッチ処理

ループ内でのAPI呼び出しはやめましょう。個々の fetch() 呼び出しはオーバーヘッドを増やし、クォータを消費します。代わりに UrlFetchApp.fetchAll() を使用してください。このメソッドは複数のリクエストを並列で送信します。各URLは依然として1日の合計にカウントされますが、fetchAll は大幅に高速であり、スクリプトがタイムアウトするリスクを軽減します。

function fetchMultipleUrls(urlList) {
  const requests = urlList.map(url => ({
    url: url,
    method: 'get',
    muteHttpExceptions: true
  }));
  
  const responses = UrlFetchApp.fetchAll(requests);
  return responses.map(res => res.getContentText());
}

ステップ3:カスタム関数からロジックを切り出す

カスタム関数はクォータを静かに使い果たす原因となります。カスタム数式を使用しているセルが500個ある場合、列をフィルタリングしたり、値を1つ変更したりするたびに、Googleがそれらすべてを再計算する可能性があります。これにより、20,000回の制限を数分で使い切ってしまうことがあります。

解決策: 数式を手動の同期ボタンに置き換えます。実際に必要なときだけデータを処理するカスタムメニューを作成しましょう。これにより、クォータの消費を自分でコントロールできるようになります。

function onOpen() {
  SpreadsheetApp.getUi().createMenu('🚀 APIツール')
    .addItem('今すぐデータを同期', 'manualSync')
    .addToUi();
}

function manualSync() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  const range = sheet.getDataRange();
  const values = range.getValues();
  
  // すべてのデータを一度に処理し、シートに一括で書き戻す
  const results = values.map(row => myProcessingLogic(row[0]));
  // ... シートを更新 ...
}

ステップ4:時間主導型トリガーの監査

Apps Script ダッシュボード > トリガー を確認してください。1分ごとに実行され、15回のAPI呼び出しを行うスクリプトは、1日に21,600回のリクエストを消費します。これはすでに個人アカウントの制限を超えています。トリガーの間隔を15分または30分に広げて、クォータに余裕を持たせましょう。

修正の確認方法

Googleはリアルタイムのクォータダッシュボードを提供していないため、積極的に確認する必要があります。Apps Script エディタの 実行数 タブを監視し、urlfetch エラーの頻度が下がっているか確認してください。また、成功したフェッチ数を非表示のシートに記録する簡単なログ機能を構築することもできます。スクリプトが機能している間にその数値が一定(プラトー)に達していれば、キャッシュロジックが正しく機能している証拠です。

プロのヒント

  • Mute Exceptionsを使用する: 常に {muteHttpExceptions: true} を設定しましょう。これにより、単一の404エラーでリクエストのバッチ全体がクラッシュするのを防げます。
  • Workspaceへのアップグレード: プロジェクトでどうしても1日50,000回以上の呼び出しが必要な場合、基本的な Workspace アカウントにアップグレードするのが、制限を5倍に増やす最も簡単な方法です。
  • プロキシへのオフロード: 膨大なデータが必要な場合は、Cloud Function を使用してデータを集約します。スクリプトはプロキシに対して1回呼び出しを行い、プロキシ側で何百ものサブリクエストを処理させます。

Related Error Notes