◉GEMINI LABEN
●3.8 FLASH — 3.7 Flash は 10/08 に非推奨に。旧モデルへの要求は 3.8 Flash へ自動で回されます●NANO 2.1 — gemini-nano-banana-2.1 が GA。gemini-3.1-flash-image は非推奨で停止日は未発表●DEADLINE — veo-3.1 の preview と gemini-omni-flash-preview は 10/22、Deep Research エージェントは 10/23 に停止(残り12〜13日)●Q&A — Gemini CLI でサブエージェントが MAX_TURNS で止まっても成功と報告される、という報告が出ています●TTS/LIVE — 2.5 系の音声・TTS 系プレビューは 11/17 に停止、残り38日●NEW — 停止日のない非推奨をどう扱うか。画像モデルの移行先を3つの条件で選び直す●3.8 FLASH — 3.7 Flash は 10/08 に非推奨に。旧モデルへの要求は 3.8 Flash へ自動で回されます●NANO 2.1 — gemini-nano-banana-2.1 が GA。gemini-3.1-flash-image は非推奨で停止日は未発表●DEADLINE — veo-3.1 の preview と gemini-omni-flash-preview は 10/22、Deep Research エージェントは 10/23 に停止(残り12〜13日)●Q&A — Gemini CLI でサブエージェントが MAX_TURNS で止まっても成功と報告される、という報告が出ています●TTS/LIVE — 2.5 系の音声・TTS 系プレビューは 11/17 に停止、残り38日●NEW — 停止日のない非推奨をどう扱うか。画像モデルの移行先を3つの条件で選び直す
記事一覧/Workspace 連携
◧ Workspace 連携/2026-06-16上級

Apps Script から Gemini でスプレッドシート数千行を処理する — 6分の実行上限を越える分割と冪等性の設計

Apps Script の6分実行上限でスプレッドシートの Gemini バッチ処理が止まる問題を、トリガー継続と冪等設計で流し切る方法をまとめました。ステータス列による1行単位の冪等化、429 の行単位隔離、時間予算を見てバトンを渡す実装を実測つきで解説します。

gemini-workspace6apps-script11gemini-api286automation36batch-processinggoogle-sheets

✦ プレミアム記事

スプレッドシートに溜まった2,000行のアプリレビューを Gemini で一気に分類・要約しようとして、実行が6分でぷつりと打ち切られたことがあります。個人開発で複数のアプリを運営していると、レビュー返信の下書きや多言語のストアメタデータの整備を、つい慣れた Google スプレッドシート上でまとめて回したくなります。ところが Apps Script で素直に書くと、行数が増えた瞬間に「処理を完了できませんでした」で止まる。しかも止まった位置が分からないので、再実行すると先頭から二重に処理してしまい、API コストだけが二重にかかる——この二つの壁を同時に外す設計を、実際に運用しているコードに沿って整理します。

6分で打ち切られる、というのが最初の壁です

Apps Script の1回の実行時間には上限があります。無料の Google アカウントで約6分、Google Workspace アカウントでも約30分です。ループの中で1行ごとに Gemini を呼ぶと、1行あたり1〜3秒かかるため、無料アカウントでは150〜300行あたりで上限に達します。

ここで多くの人がやりがちなのが「Utilities.sleep() を削ってとにかく速くする」という対処です。私自身も最初はそうしましたが、これは筋が悪いと感じています。速くしても上限が消えるわけではなく、行数が2倍になればまた止まるからです。本質的な解決は「1回の実行で全部やろうとしない」ことです。実行を時間予算内で自分から打ち切り、続きを次の実行に引き継ぐ。Apps Script にはそのための時間ベースのトリガーが用意されています。

つまり設計の方針はこうなります。

  1. シートに「処理状態」を持つ列を用意し、どの行まで終わったかをデータ自身に記録する
  2. 1回の実行は「残り時間が尽きる前」に自分で安全に停止する
  3. 停止する直前に、数十秒後に同じ関数をもう一度呼ぶトリガーを仕掛けてバトンを渡す

この3点が噛み合うと、行数が何千行になっても、複数回の実行に分割されながら最後まで流れていきます。

設計の起点は「1行ずつ冪等にする」ことです

分割実行で最も怖いのは、再開時の二重処理です。途中で止まった実行と、次に立ち上がる実行のあいだで「どこまで終わったか」の認識がずれると、同じ行に二度 Gemini を呼んでしまいます。これを防ぐ一番確実な方法は、進捗をスクリプト側の変数ではなくシートのセルそのものに書くことです。

具体的には、出力列の隣に status 列を1つ足します。値は done か error か空欄の3状態だけです。処理対象を選ぶときは「status が空欄の行」に限定します。こうすると、どのタイミングで実行が落ちても、次の実行は自然に「まだ手をつけていない行」から再開します。スクリプトの記憶に頼らないので、トリガーが二重に発火しても、片方が done を書いた行はもう片方の対象から外れます。

// 設定: 自分のシートに合わせて変更する
const CFG = {
  SHEET: 'reviews',     // 対象シート名
  COL_INPUT: 1,         // 入力テキストの列(A=1)
  COL_OUTPUT: 2,        // Gemini の結果を書く列(B=2)
  COL_STATUS: 3,        // 処理状態の列(C=3)
  HEADER_ROWS: 1,       // ヘッダー行数
  TIME_BUDGET_MS: 4.5 * 60 * 1000, // 4.5分で安全に切り上げる(6分上限の手前)
  TRIGGER_DELAY_MS: 30 * 1000,     // 30秒後に続きを実行
};
 
// status が空欄の「未処理行」だけを上から拾う
function nextPendingRows_(sheet, limit) {
  const lastRow = sheet.getLastRow();
  if (lastRow <= CFG.HEADER_ROWS) return [];
  const n = lastRow - CFG.HEADER_ROWS;
  const status = sheet.getRange(CFG.HEADER_ROWS + 1, CFG.COL_STATUS, n, 1).getValues();
  const input = sheet.getRange(CFG.HEADER_ROWS + 1, CFG.COL_INPUT, n, 1).getValues();
  const rows = [];
  for (let i = 0; i < n && rows.length < limit; i++) {
    if (!String(status[i][0]).trim() && String(input[i][0]).trim()) {
      rows.push({ row: CFG.HEADER_ROWS + 1 + i, text: String(input[i][0]) });
    }
  }
  return rows;
}

ポイントは、status を見て対象を決めている点です。出力列が埋まっているかどうかで判定すると、Gemini が空文字を返したケースを「未処理」と誤認してしまいます。状態は専用の列で明示的に持つほうが、後からの目視確認も楽になります。

✦

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

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
✦Apps Script の6分実行上限で途中停止していたバッチを、トリガー継続で数千行を最後まで流せるようになる
✦再実行のたびに同じ行を二重処理して API コストを溶かしていた構成を、ステータス列ベースの冪等処理に切り替えられる
✦429 や一時エラーで止まりがちな行単位の Gemini 呼び出しに、指数バックオフと失敗行の隔離をコピーして組み込める
Stripe による安全な決済 · いつでもキャンセル可能
✦

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

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

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

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

関連記事

◧ Workspace 連携2026-07-16
=GEMINI() を420行に貼って気づいたこと — カスタム関数をやめ、値で書き戻すまでの実測
Sheets のカスタム関数から Gemini を呼ぶ構成は、動いた瞬間がいちばん美しく、そこから伸びません。30秒の壁・制御できない再計算・書き戻せない結果という3つの性質を実測で確かめ、値で書き戻す設計へ移すまでの手順とコードを残します。
◈ API / SDK2026-07-23
会話状態をサーバーに預けるべきか — previous_interaction_id と手元での履歴刈り込みの分岐設計
Interactions API の previous_interaction_id で会話状態をサーバーに預けると、毎回履歴を送らずに済みます。ところが途中で入った巨大なツール出力を後から刈り込めず、以降のターンが重量を引きずる落とし穴があります。サーバー側状態と手元での刈り込みを状況で使い分ける分岐設計を、動くPythonの薄いラッパーとともに整理します。
◈ API / SDK2026-05-16
App Store / Google Play のレビュー返信を Gemini API で自動化したときにはまった「8秒ルール」
個人で運営するアプリ群でGemini APIによるレビュー返信自動化を試みた実装記録。多言語対応の設計と、App Storeで8秒以上待機しないとペナルティを受けるという落とし穴を共有します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます