はじめに
スプレッドシートの手作業、まだ残っていませんか。
100行のレビューをポジ/ネガ仕分けする、顧客名から営業メールを量産する、長文の問い合わせから要点だけ抜き出す——こういう「考えなくていい繰り返し」こそChatGPT APIに丸投げすべき領域です。
ただ、ググって出てくるGASコードをそのままコピペしても**「6分制限で死ぬ」「APIキーが丸見えで詰む」「途中で止まって誰も気づかない」といった事故が起きます。本記事では、その地雷を踏まずに本番運用に耐える設計**まで踏み込んで整理します。
GAS × ChatGPT APIの得意/不得意
正直に書きます。「AIをスプレッドシートに繋げば何でもできる」は過信です。
得意なケース
- データ分類・タグ付け: レビューをポジ/ネガ仕分け(手動比70〜85%短縮の事例あり)
- 文章生成: セル内容を変数化してメール文面を量産(1件5分→30秒)
- 要約・抽出: 長文問い合わせから要点抽出
- データクレンジング: 表記ゆれの統一
苦手なケース
GASの実行時間は1回6分が上限(有料Workspaceでも同じ)。
数千行のAPI呼び出しを一括処理すると途中で強制終了します。
このサイズの自動化はMake/Zapierのテンプレートのほうが向いています。
GASを書く意味があるのは「シート上のデータと密結合した処理」「社内で誰でもボタン押せばよいUI」が欲しい場面です。
詰まりポイント①:APIキーの正しい格納(ScriptPropertiesに置く)
GASコードに const KEY = "sk-..." と書いて共有→流出、はあるあるです。
正しい格納先は PropertiesService.getScriptProperties()。
画面操作で言うと「プロジェクトの設定」→「スクリプト プロパティ」に OPENAI_API_KEY のキー名で値を入れます。
function getApiKey_() {
const key = PropertiesService.getScriptProperties().getProperty('OPENAI_API_KEY');
if (!key) throw new Error('OPENAI_API_KEY未設定');
return key;
}
これだけで「コードを公開しても鍵は漏れない」状態になります。
詰まりポイント②:UrlFetchAppのoptionsで爆死しない
API呼び出しテンプレ。muteHttpExceptions: true がないと、APIエラーで処理全体が止まります。
function callChatGPT_(prompt) {
const url = 'https://api.openai.com/v1/chat/completions';
const payload = {
model: 'gpt-4o-mini',
messages: [{ role: 'user', content: prompt }],
temperature: 0.2,
max_tokens: 500,
};
const options = {
method: 'post',
contentType: 'application/json',
headers: { Authorization: 'Bearer ' + getApiKey_() },
payload: JSON.stringify(payload),
muteHttpExceptions: true, // ← 重要
};
const res = UrlFetchApp.fetch(url, options);
const code = res.getResponseCode();
if (code !== 200) {
Logger.log('API error: ' + code + ' / ' + res.getContentText());
return null; // 失敗を呼び出し元で判別できるようnull返却
}
const json = JSON.parse(res.getContentText());
return json.choices[0].message.content.trim();
}
詰まりポイント③:6分制限を「バッチ分割+トリガー」で回避
100行を一気に処理して6分で死ぬパターン。対策は「進捗をPropertiesに保存して途中再開」です。
function processBatch() {
const sheet = SpreadsheetApp.getActiveSheet();
const lastRow = sheet.getLastRow();
const props = PropertiesService.getScriptProperties();
let startRow = parseInt(props.getProperty('CURSOR') || '2', 10);
const BATCH = 20; // 1回20行ずつ
const start = Date.now();
for (let row = startRow; row <= lastRow && row < startRow + BATCH; row++) {
if (Date.now() - start > 5 * 60 * 1000) break; // 5分で打ち切り
const input = sheet.getRange(row, 1).getValue();
if (!input) continue;
const result = callChatGPT_('次を要約してください: ' + input);
if (result) sheet.getRange(row, 2).setValue(result);
startRow = row + 1;
}
props.setProperty('CURSOR', String(startRow));
}
あとは「時間ベースのトリガー」で processBatch を5〜10分間隔で起動すれば、CURSORが進みながら数千行でも完走します。
コスト感(gpt-4o-mini基準・2026年5月時点)
- 入力: $0.15 / 1Mトークン
- 出力: $0.60 / 1Mトークン
1リクエスト平均500トークン想定で 1,000行処理 ≒ $0.3〜0.5(約50〜80円) 程度。
本番投入前に必ずダッシュボードで予算上限を設定しておくのが鉄則です。
まとめ
- APIキーは必ず ScriptProperties に格納(コードに直書き禁止)
-
muteHttpExceptions: true+ ステータスコード判定で「黙って止まる」を防ぐ - 6分制限はバッチ分割+トリガー+CURSOR保存で回避