GEMINI LABEN
NANOLITE — Nano Banana 2 Liteが登場しました。Googleで最も速く、最もコスト効率の高いGemini Imageモデルで、軽量な画像生成を安く回したい用途に向いていますOMNIFLASH — Gemini Omni Flashがpublic previewになりました。ネイティブにマルチモーダルなモデルで、企業や開発者が独自の動的な動画ワークフローを構築できますAGENTS — Managed Agentsが拡張されました。background: trueでサーバー側の非同期実行とポーリング、リモートMCPサーバー連携、対話をまたぐ認証情報のリフレッシュに対応しますMEMORY — Memory BankのIngestEvents APIが一般提供になりました。イベントの取り込みとメモリ生成を分離し、コンテンツを継続的にストリームできますTHROUGHPUT — Provisioned Throughputで、同一モデル・同一リージョンに対して最大7件の保留オーダーを提出できるようになりましたDEPRECATE — 画像生成モデルは8月17日に、Gemini Enterprise Agent PlatformのGrok 4.1系は8月20日に停止される予定ですNANOLITE — Nano Banana 2 Liteが登場しました。Googleで最も速く、最もコスト効率の高いGemini Imageモデルで、軽量な画像生成を安く回したい用途に向いていますOMNIFLASH — Gemini Omni Flashがpublic previewになりました。ネイティブにマルチモーダルなモデルで、企業や開発者が独自の動的な動画ワークフローを構築できますAGENTS — Managed Agentsが拡張されました。background: trueでサーバー側の非同期実行とポーリング、リモートMCPサーバー連携、対話をまたぐ認証情報のリフレッシュに対応しますMEMORY — Memory BankのIngestEvents APIが一般提供になりました。イベントの取り込みとメモリ生成を分離し、コンテンツを継続的にストリームできますTHROUGHPUT — Provisioned Throughputで、同一モデル・同一リージョンに対して最大7件の保留オーダーを提出できるようになりましたDEPRECATE — 画像生成モデルは8月17日に、Gemini Enterprise Agent PlatformのGrok 4.1系は8月20日に停止される予定です
記事一覧/API / SDK
API / SDK/2026-05-18中級

Apps Script から Gemini API を呼ぶと長い生成で止まる原因と対処 — UrlFetchApp タイムアウトと 6 分実行上限の壁

Apps Script から Gemini API を呼んだときに UrlFetchApp のタイムアウトと 6 分の実行上限のどちらで止まっているかを見分け、分割実行・チェックポイント・トリガー再開で乗り切る実用パターンをまとめます。

Apps Script7Google Workspace15Gemini API191トラブルシューティング30タイムアウト4

2014年から運営している個人開発のアプリ事業で、AdMob 収益を Google Sheets にまとめて Gemini API で月次のサマリーを書き出す Apps Script を組んでいたことがあります。50 行程度なら問題なく動いていたのに、対象を 500 行に増やした途端、毎回違う行で処理が止まり、実行ログには Address unavailable と「スクリプトの実行時間が長すぎます」が交互に現れる、という不可解な状態に陥りました。

この症状は、Apps Script から Gemini を本格的に使い始めると多くの方がぶつかります。原因は一つではなく、二段構えの限界が同時に効いていることがほとんどです。まずは「どこで止まっているか」を切り分けるところから始めるのが近道でした。

起きているのは二種類の「タイムアウト」

Apps Script で Gemini API を呼ぶときに引っかかる限界は、大きく二つあります。

一つ目はスクリプト全体の最大実行時間です。Google アカウントの種類によって、無料アカウントは 6 分、Workspace 契約のアカウントは 30 分が上限として設定されています。これを超えると Exceeded maximum execution time という記述がログに残り、書き込み途中のセルがあれば中途半端な状態で停止します。

二つ目は UrlFetchApp.fetch のレスポンス待ちタイムアウトです。公式には明示されていませんが、おおむね 60 秒〜2 分でブロックされ、Exception: Address unavailable や DNS 関連のエラーとして表面化します。Gemini 2.5 Pro のような長文生成・長時間思考を行うモデルでは、1 リクエストでこの境界を越えてしまうことが珍しくありません。

どちらが先に発火するかは入力サイズと出力長によって変わるため、症状だけ見ると一貫性のないエラーに見えるのが厄介な点です。

まず「どちらで落ちているか」を切り分ける

エラーログを開いて、止まる直前のメッセージで判断します。

Exceeded maximum execution time が出ているなら、スクリプト全体が 6 分(または 30 分)を超えています。これはリクエスト数を抑えても、1 回ずつ長くなれば結局到達する種類のエラーです。

Address unavailable / Exception: Request failed / DNS エラーが出ているなら、UrlFetchApp.fetch の応答待ちで切れています。1 リクエスト単位の話なので、maxOutputTokens や入力サイズを見直すと改善する余地があります。

両方が混在している場合は、まず 1 リクエスト単位を縮めて UrlFetchApp 側を安定させてから、全体実行時間の問題に取り組むほうが切り分けが楽でした。

1 リクエスト単位を確実に通すための工夫

UrlFetchApp 側を安定させるための変更点は、現場で効きやすい順にいくつかあります。

function callGemini_(prompt) {
  const ENDPOINT = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro:generateContent';
  const apiKey = PropertiesService.getScriptProperties().getProperty('GEMINI_API_KEY');
 
  const body = {
    contents: [{ parts: [{ text: prompt }] }],
    generationConfig: {
      maxOutputTokens: 800,
      thinkingConfig: { thinkingBudget: 1024 },
      responseMimeType: 'application/json',
    },
  };
 
  const res = UrlFetchApp.fetch(`${ENDPOINT}?key=${apiKey}`, {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify(body),
    muteHttpExceptions: true,
  });
 
  if (res.getResponseCode() !== 200) {
    throw new Error(`Gemini API ${res.getResponseCode()}: ${res.getContentText().slice(0, 200)}`);
  }
  const json = JSON.parse(res.getContentText());
  return json.candidates?.[0]?.content?.parts?.[0]?.text ?? '';
}

ポイントは三つあります。第一に maxOutputTokens を意識的に短く設定し、1 リクエストの上限を自分でコントロールする点。第二に thinkingBudget を絞り、思考トークンが膨らんで応答時間が延びるのを抑える点。第三に responseMimeType で JSON 出力を指定して、ストリーミングではなく完全な応答を一括で受け取る前提に揃える点です。

Apps Script の UrlFetchApp は Server-Sent Events を逐次処理できないため、streamGenerateContent 系のエンドポイントは原則使えません。リアルタイム性が欲しい場合は Apps Script ではなく Cloud Functions などのバックエンドに逃がしたほうが素直です。

それでも単発で 60 秒以上かかってしまう場合は、入力プロンプトを意味のある単位で分割するのが現実的です。長い文書の要約なら段落単位、コードレビューならファイル単位、というように、ドメインに応じた切れ目を選びます。

全体実行時間を超えないためのチェックポイント方式

入力件数が多い処理では、6 分(または 30 分)の上限内に収まりきらないことが珍しくありません。私自身が Sheets の一括処理で落ち着いた構成は、PropertiesService に処理済みの行番号を保存し、時間ベーストリガーで再開する方式でした。

function runBatch() {
  const props = PropertiesService.getScriptProperties();
  const startRow = Number(props.getProperty('lastRow') || '2');
  const sheet = SpreadsheetApp.getActiveSheet();
  const lastRow = sheet.getLastRow();
 
  // 安全側に 4 分でデッドラインを切り、上限手前で能動的に降りる
  const deadline = Date.now() + 4 * 60 * 1000;
 
  for (let row = startRow; row <= lastRow; row++) {
    if (Date.now() > deadline) {
      props.setProperty('lastRow', String(row));
      scheduleNextRun_();
      return;
    }
 
    const prompt = sheet.getRange(row, 1).getValue();
    if (!prompt) continue;
    if (sheet.getRange(row, 2).getValue()) continue; // 既に書き込み済みならスキップ
 
    const result = callGemini_(prompt);
    sheet.getRange(row, 2).setValue(result);
    SpreadsheetApp.flush();
  }
 
  // 全行完了したら進捗をリセット
  props.deleteProperty('lastRow');
}
 
function scheduleNextRun_() {
  // 既存トリガーを掃除してから 1 分後に再実行を予約
  ScriptApp.getProjectTriggers()
    .filter(t => t.getHandlerFunction() === 'runBatch')
    .forEach(t => ScriptApp.deleteTrigger(t));
  ScriptApp.newTrigger('runBatch').timeBased().after(60 * 1000).create();
}

設計上の勘どころは「進捗をシートと PropertiesService の両方に書く」「自分でデッドラインを設定して上限の手前で能動的に降りる」の二点です。Google 側が強制終了するのを待つと、中途半端な状態でセルが残り、リカバリが面倒になります。

なお、トリガーは 1 プロジェクトあたり 20 個までの制限があるため、毎回新しいトリガーを作る場合は古いものを必ず削除しておきます。掃除を忘れると、ある日突然 Triggers limit exceeded で全実行が止まり、原因を追うのに時間を取られます。

API キーの取り回しと冪等性

長時間処理を組むときに見落としがちなのが、API キーの取り回しと再実行時の冪等性です。

API キーはコードに直接書かず、PropertiesService.getScriptProperties().setProperty('GEMINI_API_KEY', '...') で別管理にしておくと、リポジトリに鍵を含めずに済みます。Apps Script のエディタ画面右下の「プロジェクトの設定」からも GUI で値を編集できます。

冪等性のほうは、再開時にすでに書き込み済みのセルを上書きしない、という単純なガードで十分です。空セルだけを対象に処理することで、二重課金と上書き事故を同時に避けられます。先ほどのサンプルで if (sheet.getRange(row, 2).getValue()) continue; を入れているのは、まさにこの理由です。

本番運用に乗せる前に確認しておきたいこと

最後に、本番で運用に乗せる前に押さえておくと安心な点をまとめておきます。

  1. 1 リクエストあたりの応答時間をログに出す。極端に遅い行を特定でき、入力分割の指針になります。
  2. muteHttpExceptions: true を付けてレスポンスコードを自分で確認します。429 / 500 系で指数バックオフのリトライを組めます。
  3. 再開可能な状態でテストを止める癖をつける。エディタから手動停止すると PropertiesService の進捗が更新されないため、次回トリガー実行時に同じ行から始まらなくなります。
  4. 件数が数千行以上になる場合は、Apps Script ではなく Cloud Functions や Cloud Run に逃がす選択肢を早めに検討します。30 分の上限を超える処理を Workspace 契約で延々と引き伸ばすより、別の実行基盤に分けたほうが結局は早く済みます。

私自身、Sheets の月次処理を Cloud Functions に移しただけで、エラー対応に費やしていた時間がかなり減りました。Apps Script は「Sheets や Docs にすぐ書き戻せる軽い接着剤」として割り切り、重い処理は別の場所で動かすのが、結局は運用負荷を下げる近道だと感じています。同じところでつまずいている方のヒントになれば嬉しいです。

シェア

お読みいただきありがとうございます

Gemini Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

API / SDK2026-05-07
Gemini API で DEADLINE_EXCEEDED が頻発する時に最初に見直すべき5つのポイント
Gemini API で DEADLINE_EXCEEDED が突然頻発するときの原因切り分けと、現場で実際に効いた対処パターンを5つにまとめてご紹介します。
API / SDK2026-06-22
Gemini API × Google Cloud 本番エラーを層で切り分ける診断手順
Gemini APIをGoogle Cloud本番環境で運用する際のエラーを体系的に診断。IAM権限・Vertex AI vs AI Studio・VPC・クォータ管理・サービスアカウント設定まで、実装コード付きで完全解説します。
API / SDK2026-06-21
Gemini API モデル非推奨・移行エラーの対処法
Gemini API でモデルが非推奨になった時の移行手順と、移行時に発生するエラーの対処法を詳しく解説します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →