MAGAZINE

ルーターマガジン

AI/機械学習

Gemini API のバッチAPIで大量の処理を一括で行う話

2025.12.12
Pocket

はじめに

皆さん、こんにちは、エンジニアの Hodoshima です。

今日では、膾炙されるようになった生成 AI について、生成 AI を用いたプロダクトが多く生まれるようになっています。 その中で、生成 AI に分析を行ってもらう場合、バッチ API というものを使うと、非同期的な処理でリクエストを投げることができ、しかも料金を抑えることができます。

今回のブログでは、このバッチ API を紹介していきます。

バッチ API とは

バッチ API は公式ドキュメントによると、以下のような特徴があります。ドキュメント

  • 大量のリクエストを非同期で処理するように設計されている。
  • 料金が通常の API 料金と比較して半額になっている。
  • 目標の対応時間は 24 時間となっている (ただし、ほとんどの場合は、これより早く対応できます)

つまり、バッチ API では、即時性を犠牲にすることにより、大量のリクエストを低コストで処理することができます。

逆に、即時性が求められる場面では、バッチ API は向いていないことに注意してください。

使い方

バッチ API でリクエストを処理する場合、大きく分けて以下の 2 ステップに分けられます。

  • ステップ 1: バッチジョブを登録する。
  • ステップ 2: 登録したバッチジョブの進行状況を監視し、完了次第結果を読み込む

これ以降のコマンドについて、$GEMINI_API_KEY と書かれている部分には、このページ に沿って取得したご自身の API キーを使ってください。

ステップ 1: バッチジョブを登録する。

以下のコマンドを使うことにより、Gemini のバッチ API に対して、バッチジョブの登録を行うことができます。

curl https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:batchGenerateContent \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-X POST \
-H "Content-Type:application/json" \
-d '{
  "batch": {
    "display_name": "gemini-batch-api-sample",
      "input_config": {
          "requests": {
            "requests": [
              {
                "request": {"contents": [{"parts": [{"text": "日本で一番高い山はなんですか?"}]}]},
                "metadata": {"key": "request-1"}
              },
              {
                "request": {"contents": [{"parts": [{"text": "1 + 2 の答えは何ですか?"}]}]},
                "metadata": {"key": "request-2"}
              },
              {
                "request": {"contents": [{"parts": [{"text": "英語の root を日本語に翻訳すると何ですか?"}]}]},
                "metadata": {"key": "request-3"}
              }
            ]
          }
        }
      }
    }'

ここでは、1 つの問を聞いているリクエスト 3 個からなるバッチジョブを登録しています。

  • display_name: バッチジョブの名前を設定することができます。
  • requests: 1 つの要素が 1 回分のリクエストに対応しています。
    • metadata > key: リクエストを識別するための文字列です。自由に設定することができます。

また、各リクエストの JSON には、通常の Gemini API にて使えるパラメータの多くを使うことができます。例としては、

  • generationConfig > temperature の値を設定することによって、創造性/確実性の度合いを操ることができます。
  • request > contents >parts の配列の中に、 file_data に関する値を入れると、File API を使ったファイルの読み込みができるようになります。

バッチジョブの登録に成功すると、以下のような JSON 形式のレスポンスが返ってきます。

{
  "name": "batches/xavuzs9ju8jsf18t582eecbncgx2k8h9q958",
  "metadata": {
    "@type": "type.googleapis.com/google.ai.generativelanguage.v1main.GenerateContentBatch",
    "model": "models/gemini-2.5-flash",
    "displayName": "gemini-batch-api-sample",
    "createTime": "2025-12-11T08:52:49.792978275Z",
    "updateTime": "2025-12-11T08:52:49.792978275Z",
    "batchStats": {
      "requestCount": "3",
      "pendingRequestCount": "3"
    },
    "state": "BATCH_STATE_PENDING",
    "name": "batches/xavuzs9ju8jsf18t582eecbncgx2k8h9q958"
  }
}

特に、name に対応するバリューは次のステップで行う結果の取得の際に必要になるため、適切な方法で管理してください。

ステップ 2: 登録したバッチジョブの進行状況を監視し、完了次第結果を読み込む

ステップ 1 のバッチジョブの登録の際に取得した name$BATCH_NAME とします。

以下のコマンドを実行することにより、$BATCH_NAME に該当するバッチジョブの進行状況及び結果の取得を行うことができます。

curl https://generativelanguage.googleapis.com/v1beta/$BATCH_NAME \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-H "Content-Type:application/json"

対象のバッチジョブについて、まだ処理が始まっていない時は、次のようなレスポンスになります。

{
  "name": "batches/xavuzs9ju8jsf18t582eecbncgx2k8h9q958",
  "metadata": {
    "@type": "type.googleapis.com/google.ai.generativelanguage.v1main.GenerateContentBatch",
    "model": "models/gemini-2.5-flash",
    "displayName": "gemini-batch-api-sample",
    "createTime": "2025-12-11T09:31:46.610825676Z",
    "updateTime": "2025-12-11T09:31:46.610825676Z",
    "batchStats": {
      "requestCount": "3",
      "pendingRequestCount": "3"
    },
    "state": "BATCH_STATE_PENDING",
    "name": "batches/r9hsnai42q4ejgdjxeu8b2gt9stujscysqic"
  }
}

state の値が BATCH_STATE_PENDING である場合、バッチジョブは実行待ちの状態になっています。

バッチジョブの実行が開始されると、state の値が JOB_STATE_RUNNING に切り替わります。

そして、処理が完了した場合、state の値が BATCH_STATE_SUCCEEDED に切り替わり、同時に、各リクエストに対するレスポンスが付与された JSON が返ってきます。

{
  "name": "batches/xavuzs9ju8jsf18t582eecbncgx2k8h9q958",
  "metadata": {
    "@type": "type.googleapis.com/google.ai.generativelanguage.v1main.GenerateContentBatch",
    "model": "models/gemini-2.5-flash",
    "displayName": "gemini-batch-api-sample",
    "output": {
      (省略)
    },
    "createTime": "2025-12-11T08:52:49.792978275Z",
    "endTime": "2025-12-11T08:53:58.641268135Z",
    "updateTime": "2025-12-11T08:53:58.641268094Z",
    "batchStats": {
      "requestCount": "3",
      "successfulRequestCount": "3"
    },
    "state": "BATCH_STATE_SUCCEEDED",
    "name": "batches/xavuzs9ju8jsf18t582eecbncgx2k8h9q958"
  },
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.ai.generativelanguage.v1main.GenerateContentBatchOutput",
    "inlinedResponses": {
      "inlinedResponses": [
        {
          "response": {
            "candidates": [
              {
                "content": {
                  "parts": [
                    {
                      "text": "日本で一番高い山は**富士山(ふじさん)**です。\n\n標高は**3,776メートル**です。日本を象徴する山であり、世界遺産にも登録されています。"
                    }
                  ],
                  "role": "model"
                },
                "finishReason": "STOP",
                "index": 0
              }
            ],
            "usageMetadata": {
              "promptTokenCount": 9,
              "candidatesTokenCount": 46,
              "totalTokenCount": 289,
              "promptTokensDetails": [
                {
                  "modality": "TEXT",
                  "tokenCount": 9
                }
              ],
              "thoughtsTokenCount": 234
            },
            "modelVersion": "gemini-2.5-flash",
            "responseId": "noY6aaTbI_bb_uMP2rjYgQ0"
          },
          "metadata": {
            "key": "request-1"
          }
        },
        {
          "response": {
            "candidates": [
              {
                "content": {
                  "parts": [
                    {
                      "text": "1 + 2 の答えは **3** です。"
                    }
                  ],
                  "role": "model"
                },
                "finishReason": "STOP",
                "index": 0
              }
            ],
            "usageMetadata": {
              "promptTokenCount": 9,
              "candidatesTokenCount": 12,
              "totalTokenCount": 59,
              "promptTokensDetails": [
                {
                  "modality": "TEXT",
                  "tokenCount": 9
                }
              ],
              "thoughtsTokenCount": 38
            },
            "modelVersion": "gemini-2.5-flash",
            "responseId": "nYY6aYmbJce5qtsPrIHJkAU"
          },
          "metadata": {
            "key": "request-2"
          }
        },
        {
          "response": {
            "candidates": [
              {
                "content": {
                  "parts": [
                    {
                      "text": "英語の \"root\" を日本語に翻訳する場合、文脈によっていくつかの表現が考えられます。特に「英語の単語のroot」という文脈であれば、以下の2つが最も適切です。\n\n1.  **語源 (ごげん - gogen)**\n    *   これは「単語の由来」「起源」という意味で、最も一般的に使われます。\n    *   例:「多くの英単語はラテン語を**語源**としています。」(Many English words have Latin **roots**.)\n\n2.  **語根 (ごこん - gokon)**\n    *   これは言語学的な専門用語で、単語の意味の中核をなす部分(接頭辞や接尾辞を取り除いた、それ以上分解できない意味を持つ部分)を指します。\n    *   例:「'ject' は『投げる』という意味の**語根**です。」( 'ject' is a **root** meaning 'throw'.)\n\n**使い分けのニュアンス:**\n\n*   **語源 (gogen)**:より広く「その単語がどこから来たか、どの言語に由来するか」という歴史的・起源的な意味合いで使われます。\n*   **語根 (gokon)**:より構造的・形態学的な観点から「単語の核となる意味要素」を指す場合に用いられます。\n\n多くの日常的な会話や学習の文脈では、「**語源**」を使うのが自然で分かりやすいでしょう。\n\n**その他(より広い意味での \"root\"):**\n\nもし「英語のroot」が単に「root」という単語の翻訳であれば、以下のような意味もあります。\n\n*   **根 (ね - ne)**:植物の根、歯の根など、物理的な「根」。\n*   **根源 (こんげん - kongen)**:問題の根源、事の始まりなど、抽象的な「根本、起源」。\n\nしかし、ご質問の「英語の root」という文脈からは、上記で挙げた「語源」や「語根」が最も適切です。"
                    }
                  ],
                  "role": "model"
                },
                "finishReason": "STOP",
                "index": 0
              }
            ],
            "usageMetadata": {
              "promptTokenCount": 11,
              "candidatesTokenCount": 449,
              "totalTokenCount": 1520,
              "promptTokensDetails": [
                {
                  "modality": "TEXT",
                  "tokenCount": 11
                }
              ],
              "thoughtsTokenCount": 1060
            },
            "modelVersion": "gemini-2.5-flash",
            "responseId": "poY6afn7GZjXz7IPwJvdwQU"
          },
          "metadata": {
            "key": "request-3"
          }
        }
      ]
    }
  }
}

response > inlinedResponses > inlinedResponses の配列に格納されているそれぞれのオブジェクト 1 個が、1 つのリクエストに対するレスポンスに対応しています。

例えば、最初の要素であった、

{
  "response": {
    "candidates": [
      {
        "content": {
          "parts": [
            {
              "text": "日本で一番高い山は**富士山(ふじさん)**です。\n\n標高は**3,776メートル**です。日本を象徴する山であり、世界遺産にも登録されています。"
            }
          ],
          "role": "model"
        },
        "finishReason": "STOP",
        "index": 0
      }
    ],
    "usageMetadata": {
      "promptTokenCount": 9,
      "candidatesTokenCount": 46,
      "totalTokenCount": 289,
      "promptTokensDetails": [
        {
          "modality": "TEXT",
          "tokenCount": 9
        }
      ],
      "thoughtsTokenCount": 234
    },
    "modelVersion": "gemini-2.5-flash",
    "responseId": "noY6aaTbI_bb_uMP2rjYgQ0"
  },
  "metadata": {
    "key": "request-1"
  }
}

は、キーが metadata > keyrequest-1 であるリクエスト。つまり、日本一高い山を訪ねたリクエストに対応するレスポンスになります。

各レスポンスに対応している JSON は、通常の API にリクエストを送った場合のレスポンスとほとんど一致しています。つまり、以下のことが読み取れます。

  • レスポンスのテキストが「日本で一番高い山は**富士山(ふじさん)**です。\n\n標高は**3,776メートル**です。日本を象徴する山であり、世界遺産にも登録されています。」であること
  • このリクエストとレスポンスに対するトークンの使用量
    • プロンプトトークン (promptTokenCount): 9
    • レスポンストークン (candidatesTokenCount): 46
    • 思考トークン (thoughtsTokenCount): 234

そのため、取得した結果を for 文で metadata > key で対応するリクエストを探しながら回すことによって、一度に結果の集計を行うことができます。

バッチ API を使うことによるメリット

通常の Gemini API を使う場合と比較して、バッチ API を利用することによって、以下の恩恵を受けられます。

  • API の料金が半額になります。特に、大量のデータ分析で使う場合には重大なコストダウンにつながります。
  • プログラムを実装する際、ステップ 1 とステップ 2 はそれぞれ独立したプログラムで書くことができます。これにより、それぞれのプログラムでの責務を小さくすることができ、コンパクトなシステム設計をすることができます。

一方で、バッチ API を使う場合、以下のことに注意してください。

  • 目標時間は 24 時間とありますが、必ずしも 24 時間で終わる保証はありません。
  • 実行待ちまたは実行中のステータスとして持てるバッチジョブの数は 100 個までです。
  • リクエストの数が増えると、それだけ処理の時間がかかり、動作が不安定になる可能性があります。バッチジョブに入れるリクエストの数は適宜調節してください。

最後に

Gemini では、バッチ API を使うことによって、低コストで沢山の処理を行うことができます。バッチ API では、特に大量の分析などを行う場合に非常に役に立ちます。

実は、この機能は今年 2025 年の 7月にリリースされた、比較的新しめの機能になります。ですので、この機能をうまく利用すると、他者より一歩リードできるかもしません。

皆さんも、ぜひバッチ API を有効活用して、大量のリクエストを低コストで処理してみてください。

Pocket

CONTACT

お問い合わせ・ご依頼はこちらから