問題の概要
このエラーが発生すると、まるで壁にぶつかったような感覚に陥ります。これは、スクリプトが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回呼び出しを行い、プロキシ側で何百ものサブリクエストを処理させます。

