はじめに
Gemini API の Batch API・Deep Research・動画生成といった処理は、完了まで数分〜数時間かかることがあります。これまで開発者は「ジョブが終わったかどうか」を知るために GET /operations を繰り返し呼び出す ポーリング に頼る必要がありました。
2026年5月4日、Google は Gemini API に イベント駆動 Webhooks を追加し、ポーリングを不要にしました。ジョブが完了した瞬間に、Gemini API 側からあなたのサーバーへ HTTP POST がプッシュされる仕組みです。本記事では、この Webhooks の全体像・設定方法・署名検証の実装までを公式ドキュメントに基づいて解説します。
この記事で学べること
- ポーリングと Webhooks(プッシュ通知)の違いと、なぜ Webhooks が有利なのか
- 静的 Webhook と動的 Webhook の使い分け
- 対応イベントの一覧と、ペイロードの構造
- 署名検証(リプレイ攻撃対策)の実装方法
対象読者
- Gemini API の Batch API や動画生成など、長時間ジョブを扱う開発者
- ポーリング処理のコスト・複雑さに悩んでいる方
- Webhook を安全に受信する実装パターンを知りたい方
前提知識・環境
- Python 3.10 以上
- Gemini API キー(Google AI Studio で取得)
- HTTP POST を受け取れるエンドポイント(自前サーバー、または検証用のトンネリングツール)
TL;DR
- Gemini API に イベント駆動 Webhooks が追加され、長時間ジョブの完了をプッシュ通知で受け取れるようになった(2026-05-04発表・全ユーザー提供開始)。
- ポーリング(
GET /operationsの連打)が不要になり、レイテンシとリクエストコストを削減できる。 - 設定方法は 静的Webhook(プロジェクト単位・HMAC)と 動的Webhook(リクエスト単位・JWKS)の2種類。
-
Standard Webhooks 仕様に準拠し、
webhook-signature/webhook-id/webhook-timestampヘッダーで署名検証・リプレイ対策を行う。
背景: ポーリングの何が問題だったのか
Gemini API がエージェント的ワークフローや大量処理にシフトするにつれ、1回の処理が数分〜数時間に及ぶケースが増えています。Google 公式ブログは次のように説明しています。
As Gemini shifts toward agentic workflows and high-volume processing — like Deep Research, long video generation, or processing thousands of prompts via the Batch API — operations can take minutes or even hours.
— Reduce friction and latency for long-running jobs with Webhooks in Gemini API(2026-05-04)
従来のポーリング方式には以下の課題がありました。
| 課題 | 内容 |
|---|---|
| 無駄なリクエスト | 完了していなくても定期的に GET を投げ続ける必要がある |
| レイテンシ | ポーリング間隔の分だけ完了検知が遅れる |
| 実装の複雑さ | バックオフ・タイムアウト・状態管理を自前で書く必要がある |
Webhooks はこれらを、Gemini API 側からの プッシュ で解決します。ジョブが完了した瞬間に、署名付きの HTTP POST があなたのサーバーへ届きます。
Webhooks の全体像
Webhooks の基本フローは次のとおりです。
- 長時間ジョブ(Batch API・動画生成など)を作成する
- ジョブが完了すると、Gemini API があらかじめ登録したエンドポイントへ HTTP POST を送る
- サーバーは署名を検証し、ペイロードに含まれる結果(出力ファイルのURIなど)を使って後続処理を行う
配信は at-least-once(最低1回) が保証されており、失敗時には最大24時間の自動リトライが行われます。同じイベントが複数回届く可能性があるため、後述する webhook-id を使った冪等な処理が重要です。
対応イベント一覧
公式ドキュメントによると、現在サポートされているイベントは以下のとおりです。
| イベント名 | 意味 |
|---|---|
batch.succeeded |
Batch ジョブが正常完了 |
batch.cancelled |
Batch ジョブがキャンセルされた |
batch.expired |
Batch ジョブが24時間以内に処理されず期限切れ |
batch.failed |
システムエラーまたはバリデーションエラー |
interaction.requires_action |
関数呼び出し(function call)待ち |
interaction.completed |
長時間オペレーション(LRO)が成功 |
interaction.failed |
LRO がエラー |
interaction.cancelled |
LRO がキャンセルされた |
video.generated |
動画生成が完了 |
ペイロードの構造
Webhook で届く JSON は、結果そのものではなく「結果への参照」を含む薄いモデルになっています。
{
"type": "batch.succeeded",
"version": "v1",
"timestamp": "2026-01-22T12:00:00Z",
"data": {
"id": "batch_123456",
"output_file_uri": "gs://my-bucket/results.jsonl"
}
}
data.id でジョブを特定し、output_file_uri などの参照から実際の結果を取得する流れになります。
静的 Webhook と動的 Webhook
Gemini API では、Webhook の登録方法が2種類用意されています。用途に応じて使い分けます。
| 種類 | 登録単位 | 署名方式 | 向いている用途 |
|---|---|---|---|
| 静的(Static) | プロジェクト単位 | HMAC(対称鍵) | Slack通知・DB同期など、全ジョブ共通のグローバル連携 |
| 動的(Dynamic) | リクエスト単位 | JWKS(非対称署名) | 特定ジョブを専用エンドポイントへルーティング |
静的 Webhook
プロジェクト単位のエンドポイントを WebhookService API で登録します。登録時に返される署名シークレットは その場でしか取得できない ため、安全に保管する必要があります。
署名シークレット(
new_signing_secret)は作成時に一度だけ返されます。再表示できないため、シークレットマネージャ等へ必ず保存してください。
動的 Webhook
ジョブ作成時に webhook_config パラメータでエンドポイントURLを指定します。リクエストごとに送信先を切り替えたい場合に便利です。動的 Webhook の署名は https://generativelanguage.googleapis.com/.well-known/jwks.json の公開鍵(JWKS)を使った非対称署名(JWT)で検証します。
署名検証の実装
Webhook は誰でも POST できるエンドポイントに届くため、必ず署名を検証 してから処理する必要があります。Gemini API の Webhooks は Standard Webhooks 仕様に準拠しており、以下のヘッダーが付与されます。
| ヘッダー | 役割 |
|---|---|
webhook-signature |
署名(HMAC または JWT) |
webhook-id |
リクエストの一意ID(重複排除=冪等性に利用) |
webhook-timestamp |
タイムスタンプ(リプレイ攻撃対策に利用) |
リプレイ攻撃対策
公式ドキュメントでは、webhook-timestamp を検証して 5分より古いペイロードを拒否する ことが推奨されています。
Always validate this timestamp on your server configuration layer to reject payloads older than 5 minutes
— Webhooks | Gemini API
Python での検証例
静的 Webhook の署名検証は、standardwebhooks ライブラリを使うと簡潔に書けます。
pip install standardwebhooks
from standardwebhooks.webhooks import Webhook
# 作成時に取得した署名シークレットを安全に読み込む
SIGNING_SECRET = "whsec_..."
wh = Webhook(SIGNING_SECRET)
# payload は生のリクエストボディ(bytes/str)、headers は受信したHTTPヘッダー
event = wh.verify(payload, headers)
# verify が成功すれば、検証済みのイベントペイロードが返る
verify() は署名・タイムスタンプの検証を内部で行い、検証に失敗すると例外を送出します。Flask での受信ハンドラに組み込むと、次のようなイメージになります。
from flask import Flask, request, abort
from standardwebhooks.webhooks import Webhook
app = Flask(__name__)
SIGNING_SECRET = "whsec_..."
wh = Webhook(SIGNING_SECRET)
# webhook-id を記録しておき、同じIDの再配信は冪等にスキップする想定
processed_ids = set()
@app.route("/gemini/webhook", methods=["POST"])
def gemini_webhook():
payload = request.get_data() # 生のボディ
headers = request.headers # webhook-* ヘッダー(case-insensitiveに参照可能)
try:
event = wh.verify(payload, headers)
except Exception:
abort(400) # 署名検証に失敗したら拒否
webhook_id = headers.get("webhook-id")
if webhook_id in processed_ids:
return "", 200 # 重複配信は冪等にスキップ
processed_ids.add(webhook_id)
if event["type"] == "batch.succeeded":
# event["data"]["output_file_uri"] から結果を取得する処理
pass
return "", 200
webhook-idを使った重複排除は、Webhook が at-least-once 配信である以上ほぼ必須です。本番環境ではprocessed_idsをメモリではなく Redis やデータベースなど永続的なストアで管理してください。
まとめ
- Gemini API の イベント駆動 Webhooks により、Batch API・Deep Research・動画生成などの長時間ジョブの完了を、ポーリングなしでプッシュ通知として受け取れるようになりました。
- 用途に応じて 静的Webhook(プロジェクト単位・HMAC) と 動的Webhook(リクエスト単位・JWKS) を使い分けます。
- 配信は at-least-once のため、
webhook-idによる冪等処理と、webhook-timestampによる5分のリプレイ対策が安全運用の鍵です。 - 署名検証は Standard Webhooks 準拠で、
standardwebhooksライブラリを使えば数行で実装できます。
ポーリングのために書いていたバックオフ・状態管理コードを削減できるため、長時間ジョブを扱うアプリケーションのアーキテクチャをシンプルにできます。まずは検証用エンドポイントで batch.succeeded を受け取るところから試すのがおすすめです。
参考リンク
- Reduce friction and latency for long-running jobs with Webhooks in Gemini API(Google公式ブログ) — 発表内容・解決する課題の引用元
- Webhooks | Gemini API | Google AI for Developers — イベント一覧・署名検証・ペイロード構造の出典
- Release notes | Gemini API — Webhooks 提供開始の記録
- Standard Webhooks — 署名仕様の準拠先