2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Gemini API Webhooks入門 — ポーリング不要で長時間ジョブの完了を受け取る

2
Last updated at Posted at 2026-06-29

はじめに

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 の基本フローは次のとおりです。

  1. 長時間ジョブ(Batch API・動画生成など)を作成する
  2. ジョブが完了すると、Gemini API があらかじめ登録したエンドポイントへ HTTP POST を送る
  3. サーバーは署名を検証し、ペイロードに含まれる結果(出力ファイルの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 動画生成が完了

出典: Webhooks | Gemini API

ペイロードの構造

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 を受け取るところから試すのがおすすめです。

参考リンク

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?