Googleスプレッドシート│Gemini APIの無料枠を使う方法

スプレッドシートにAI関数が入ったものの、正直使いづらいので

大量の処理をするにはGemini APIを使うほうがいいのでは?ということで試してみました。

目次

Googleスプレッドシートで無料枠のGemini APIを使う方法

ハマるとずっと返答がないままなので、手順を踏んで着実に確かめていきましょう。

2系と2.5系で記述が変わったりするので、バージョンによってコードのメンテナンスが必要です。

Gemini APIキーの取得

まずはGoogle AI Studioにアクセスして右上からAPIキーを作成ボタンを押します。

https://aistudio.google.com/app/api-keys?hl=ja

プロジェクト名とキーの名前を適当に入れます。

例:Google Sheets

APIキーをコピーしてメモしておきましょう。

無料枠をつかうだけなら、支払いの設定やクレジットカードの登録は不要です!

スクリプトプロパティの設定

Googleスプレッドシートを作成

拡張機能 > Apps Scriptを開きます。

GEMINI_API_KEYという名前でスクリプトプロパティに先ほど取得したAPIキーを登録します。

予備知識

ちなみに無料枠で使える制限は以下の通り。

モデル名主な無料枠レート制限補足/注意点
Gemini 2.5 Flash・RPM(1分あたりリクエスト数):約 10回/分
・TPM(1分あたりトークン数):約 250,000トークン/分
・RPD(1日あたりリクエスト数):約 250回/日
無料枠で「比較的高性能モデル」を使いたい場合の第一候補。だが “10回/分” が上限なので、GASでループ処理・連続呼び出しするなら少し間隔を空ける設計を。
Gemini 2.5 Flash-Lite・RPM:約 15回/分
・TPM:約 250,000トークン/分 (もしくは同じくらい)
・RPD:約 1,000回/日
コスト・スループット重視の場合に適。精度・速度のバランスを考慮。要約/大量データ処理にはこの “Lite” が候補。
Gemini 2.5 ProFree Tier における “公式明記の完全なレート値” は少し曖昧だが、ドキュメント上では:
・RPM:2回/分
・TPM:125,000トークン/分
・RPD:50回/日
最上位モデル。無料枠ではかなり制限が厳しいので、テスト用途や高精度・重めタスクのときのみ候補。頻繁に呼び出す用途には無料枠では厳しい可能性があります。

テスト

テストコードを実行していきます。

コードは最後に掲載

初回は認証があるので、まずは許可しておきましょう。

1) 最小通信テスト(Hello)

test1_minimalHello()

を実行します。

11:15:41	お知らせ	実行開始
11:15:42	情報	HTTP ステータス: 200
11:15:42	情報	レスポンス本文: {
  "candidates": [
    {
      "content": {
        "parts": [
          {
            "text": "こんにちは!GASからGeminiへの通信テストですね。\n\nどのようなテストをしたいか、具体的に教えていただけますか?\n\n例えば、以下のような情報があると、より的確なアドバイスができます。\n\n*   **Gemini API のどの機能を試したいか?** (テキスト生成、画像生成、埋め込みなど)\n*   **GAS でどのような処理をしたいか?** (スプレッドシートのデータを Gemini に渡す、Gemini の結果をスプレッドシートに書き込むなど)\n*   **現在どのようなコードを書いていて、どのような問題に直面しているか?**\n\n"
          }
        ],
        "role": "model"
      },
      "finishReason": "MAX_TOKENS",
      "avgLogprobs": -0.18370489346778998
    }
  ],
  "usageMetadata": {
    "promptTokenCount": 11,
    "candidatesTokenCount": 118,
    "totalTokenCount": 129,
    "promptTokensDetails": [
      {
        "modality": "TEXT",
        "tokenCount": 11
      }
    ],
    "candidatesTokensDetails": [
      {
        "modality": "TEXT",
        "tokenCount": 118
      }
    ]
  },
  "modelVersion": "gemini-2.0-flash",
  "responseId": "zb8jad_nGM_jz7IPtYn28Qc"
}
11:15:43	お知らせ	実行完了

HTTP ステータス: 200で問題なく無料枠のAPIが使えそうです。

2) 汎用プロンプトテスト(短い要約)

test2_promptEcho()を実行

11:18:44	お知らせ	実行開始
11:18:44	情報	sample (gemini-2.0-flash): SWELL特化、SEOに強いWordPress制作会社
11:18:44	情報	Gemini 出力: SWELL特化、SEOに強いWordPress制作会社
11:18:45	お知らせ	実行完了

文章の要約が出力されました。

端的で、素晴らしい回答。

3) モデル一覧取得テスト

test3_listModels()を実行します。

無料枠で使えるモデル名が診断_モデル一覧というシートに出力されました。

色々なモデルが用意されていますね。

4) シート要約テスト

シート1か適当なシートに以下のサンプルデータをコピペします。

IDタイトルステータス担当者
101トップページUI調整進行中佐藤
102Figmaデザイン最終確認レビュー中佐藤
103お問い合わせフォーム修正完了鈴木
104画像圧縮と最適化進行中鈴木
105SEO初期設定未着手田中
106ブログ一覧レイアウト改善進行中田中
107スマホ表示の崩れ修正レビュー中佐藤
108CTAボタンデザイン変更完了鈴木
109構造化データの追加進行中田中
110フッター情報更新未着手佐藤

test4_summarizeSheet()を実行します。

担当者別まとめ(要約)というシートが作成されて、要約がまとめられました。

無料でシートの内容をまとめてくれるので便利ですね!

GASなのでトリガーで定期実行もできます。

最後に今回のコードです。

GASのコード全文

/************************************************************
 * GAS × Gemini API 段階テスト一式(AI Studio 無料キー用)
 * - エンドポイント: v1beta
 * - APIキーはヘッダー x-goog-api-key で渡す
 *
 ************************************************************/

const GEMINI_MODEL_PRIMARY   = 'gemini-2.5-flash'; 
const GEMINI_MODEL_ALTERNATE = 'gemini-flash-latest';
const GEMINI_ENDPOINT_BASE   = 'https://generativelanguage.googleapis.com/v1beta/models';

/** 1) 最小通信テスト(Hello) */
function test1_minimalHello() {
  const key = getKey_();
  const url = `${GEMINI_ENDPOINT_BASE}/${GEMINI_MODEL_PRIMARY}:generateContent`;

  const payload = {
    contents: [
      {
        role: 'user',
        parts: [{ text: 'こんにちは!GAS から Gemini への通信テストです。' }]
      }
    ],
    generationConfig: {
      temperature: 0.2,
      maxOutputTokens: 128,
      responseMimeType: 'text/plain'
    }
  };

  const res = UrlFetchApp.fetch(url, {
    method: 'post',
    contentType: 'application/json',
    headers: { 'x-goog-api-key': key },
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  });

  Logger.log('HTTP ステータス: ' + res.getResponseCode());
  Logger.log('レスポンス本文: ' + res.getContentText());
}

/** 2) 汎用プロンプトテスト(短い要約) */
function test2_promptEcho() {
  const text = 'この文章を15文字以内で要約してください:MOTOKI合同会社は、WordPressに特化したWeb制作会社で、SEOに強いサイト構築を強みとしています。WordPressテーマ「SWELL」をベースに、ヒアリングを重視した機能・デザイン・使いやすさを追求したウェブサイト制作を行っています。 事業内容: Webサイトの制作。得意分野: WordPress、特に「SWELL」を用いた制作。強み:長年のサイト運営経験を活かしたSEOに強いサイト構築。顧客へのヒアリングを重視し、機能、デザイン、使いやすさを追求したサイト制作。内部施策の強化によるSEO対策。所在地: 埼玉県所沢市。';
  const out = callGemini_(GEMINI_MODEL_PRIMARY, text);
  Logger.log('Gemini 出力: ' + out);
}

/** 3) モデル一覧取得テスト(generateContent対応を確認) */
function test3_listModels() {
  const key = getKey_();
  const url = `${GEMINI_ENDPOINT_BASE}?key=${encodeURIComponent(key)}`;
  const res = UrlFetchApp.fetch(url, { muteHttpExceptions: true });

  Logger.log('HTTP ステータス: ' + res.getResponseCode());
  Logger.log('レスポンス本文: ' + res.getContentText());

  // 必要ならスプレッドシートに展開
  const json = JSON.parse(res.getContentText());
  const arr  = json.models || [];
  const rows = [['name', 'supportedGenerationMethods']];

  arr.forEach(m => {
    rows.push([
      m.name || '',
      (m.supportedGenerationMethods || []).join(', ')
    ]);
  });

  const ss = SpreadsheetApp.getActive();
  const sh =
    ss.getSheetByName('診断_モデル一覧') ||
    ss.insertSheet('診断_モデル一覧');

  sh.clear();
  sh.getRange(1, 1, rows.length, 2).setValues(rows);
  sh.autoResizeColumns(1, 2);
}

/**
 * 4) シート要約テスト
 * - 対象シート: 「シート1」
 * - 列構成: A=ID, B=タイトル, C=ステータス, D=担当者
 * - 出力: 「担当者別まとめ(要約)」シートに担当者ごとの要約を書き出し
 */
function test4_summarizeSheet() {
  const ss  = SpreadsheetApp.getActive();
  const src = ss.getSheetByName('シート1');
  if (!src) throw new Error('「シート1」が見つかりません。');

  const lastRow = src.getLastRow();
  if (lastRow < 2) throw new Error('2行目以降にデータがありません。');

  const values = src.getRange(2, 1, lastRow - 1, 4).getValues(); // A:D
  const byAssignee = new Map();

  for (const [id, title, status, assignee] of values) {
    if (!assignee) continue;
    const line = `${id}|${title}|${status}`;
    if (!byAssignee.has(assignee)) byAssignee.set(assignee, []);
    byAssignee.get(assignee).push(line);
  }

  const out   = [['担当者', '件数', '要約']];
  const names = Array.from(byAssignee.keys())
    .sort((a, b) => String(a).localeCompare(String(b), 'ja'));

  for (const name of names) {
    const tasks  = byAssignee.get(name);
    const prompt = buildSummaryPrompt_(tasks);

    // まずは安定モデル
    let summary = callGemini_(GEMINI_MODEL_PRIMARY, prompt);

    // 結果が空 or エラー表示の場合は代替モデルも試す
    if (!summary || /^(エラー:|(結果なし)/.test(summary)) {
      Utilities.sleep(200);
      summary = callGemini_(GEMINI_MODEL_ALTERNATE, prompt);
    }

    out.push([name, tasks.length, summary || '(結果なし)']);
    Utilities.sleep(250); // 連続呼び出しの軽い間隔
  }

  let dst = ss.getSheetByName('担当者別まとめ(要約)');
  if (!dst) dst = ss.insertSheet('担当者別まとめ(要約)');
  else dst.clear();

  dst.getRange(1, 1, out.length, 3).setValues(out);
  dst.autoResizeColumns(1, 2);
  dst.setColumnWidth(3, 720);
  dst.getRange('A1:C1').setFontWeight('bold');
}

/* ====== 共通ユーティリティ ====== */

/** タスク配列から要約用のプロンプトを組み立て */
function buildSummaryPrompt_(items) {
  return `
以下のタスクを日本語で要約してください。

【必ず守るルール】
・必ず文章を出力する
・空欄は禁止
・最大120文字以内
・1〜2文 + 箇条書き(最大3項目)

【出力形式】
進捗まとめ:
・〇〇
・〇〇

【タスク】
${items.map(s => '・' + s).join('\n')}
`;
}


/** Gemini 呼び出し共通処理(プレーンテキスト前提) */
function callGemini_(model, userText) {
  const key = getKey_();
  const url = `${GEMINI_ENDPOINT_BASE}/${model}:generateContent`;

  const payload = {
    contents: [
      {
        role: 'user',
        parts: [{ text: userText }]
      }
    ],
    generationConfig: {
      temperature: 0.2,
      maxOutputTokens: 300
    }
  };

  const res = UrlFetchApp.fetch(url, {
    method: 'post',
    contentType: 'application/json',
    headers: { 'x-goog-api-key': key },
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  });

  const code = res.getResponseCode();
  const body = res.getContentText();

  if (code !== 200) {
    Logger.log(`Gemini Error ${code}: ${body}`);
    return `(エラー: ${code})`;
  }

  try {
    const json = JSON.parse(body);

    // 2.5世代向けの最も安全な取得方法
    const parts = json?.candidates?.[0]?.content?.parts || [];

    if (!parts.length) {
      Logger.log('Empty response body:' + body);
      return '(結果なし)';
    }

    const text = parts.map(p => p.text || '').join('').trim();

    if (!text) {
      Logger.log('Empty text:' + body);
      return '(結果なし)';
    }

    return text;

  } catch (e) {
    Logger.log('JSON parse error: ' + e);
    return '(結果なし)';
  }
}


/** スクリプトプロパティから API キー取得 */
function getKey_() {
  const key = PropertiesService.getScriptProperties().getProperty('GEMINI_API_KEY');
  if (!key) {
    throw new Error('GEMINI_API_KEY が未設定です(スクリプトプロパティに API キーを設定してください)。');
  }
  return key;
}

以上です。

まとめ

スプレッドシートのAI関数もでてきたのですが、やっぱりAPIで処理するほうが、表現の幅や物量もこなせるので便利です。

ぜひ試してみてください。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

WAZAの有料記事のサブスクリプションも開始しました。

目次