Gemini

GoでGemini APIを使う実装手順|SDK v1.77・Interactions API・エンドポイントURL

GoでGemini APIを使う実装手順|SDK v1.77・Interactions API・エンドポイントURL

GoからGemini APIを呼ぶサンプルのうち、旧SDKや提供終了したモデルを使うものは、そのままでは動かない場合があります。実装前にimportパスとモデルIDを確認してください。この記事では現行SDK google.golang.org/genai v1.77.0(2026年10月29日)を前提に、テキスト生成、エンドポイントURLの構造、2026年10月にGo SDKへ入ったInteractions API、並行呼び出しと再試行の設計までを、SDKのソースと公式ドキュメントで確かめた内容で整理します。

まとめ

GoでGemini APIを使うなら、SDKは google.golang.org/genai 一択です。旧SDK github.com/google/generative-ai-go は2025年11月30日にサポートが終わり、リポジトリもアーカイブ済みです。

2026年11月時点で押さえておくべき変化は4つあります。1つ目に、Go SDKはv1.72.0(2026年10月1日)でInteractions APIに対応し、client.Interactions.Create で会話をサーバー側に持たせられるようになりました。2つ目に、新規プロジェクトでは gemini-2.5-flash などの2.5系が使えず、公式は gemini-3.8-flash か gemini-3.5-flash-lite を案内しています。3つ目に、Models系のメソッドは既定では再試行せず、Interactions系は既定で最大4回再試行するという非対称があります。4つ目に、エラー型も前者は値型、後者はポインタ型で、errors.As の書き方が変わります。

Gemini API自体の定義・料金・始め方はGemini APIとは?できること・料金・始め方とInteractions APIの最小コードにまとめています。この記事はGoで実装するときの判断に絞ります。

旧SDK generative-ai-go の見分け方と移行先

古いサンプルは import パスで見分けられます。github.com/google/generative-ai-go/genai を import しているコードは旧SDKです。Gemini APIの公式ライブラリページは旧ライブラリを「2025年11月30日をもって非推奨」と位置づけ、Goの推奨を google.golang.org/genai としています。旧SDKはLive APIやVeoといった新しい機能にも対応していません。

EOL日を調べるときは参照元に注意してください。generative-ai-goのリポジトリのREADMEでは、EOL日が当初の2025年8月31日から9月30日、11月30日へと二度延長されています。pkg.go.devが表示するのは最終タグv0.20.1(2025年5月2日)時点のREADMEなので、そこには古い「8月31日」が残っています。

移行先の go-genai はリリースが続いており、v1.66.0(2026年7月28日)からv1.77.0までの3か月で11回更新されています。go.mod は go 1.24 を要求するので、それより古いGoでは導入できません。言語側の更新状況はGo 1.27の新機能・変更点まとめで確認できます。

Gemini APIをGoから呼ぶ最小実装

SDKの導入とAPIキーの読み込み優先順位

go get google.golang.org/[email protected]

APIキーは ClientConfig.APIKey に直接渡すか、環境変数から読ませます。SDKは GOOGLE_API_KEY と GEMINI_API_KEY の両方を参照し、両方セットされていると警告ログを出して GOOGLE_API_KEY を採用します(client.go の getAPIKeyFromEnv)。CIで別のGoogle系サービス用に GOOGLE_API_KEY が入っていると、GEMINI_API_KEY に正しいキーを入れても別のキーで認証されて失敗します。どちらか一方だけを定義してください。キーの発行と制限のかけ方はGemini APIキーの取得方法で手順を追えます。

GenerateContentの最小コード

package main

import (
    "context"
    "fmt"
    "log"

    "google.golang.org/genai"
)

func main() {
    ctx := context.Background()

    // APIKey を省略すると GOOGLE_API_KEY、無ければ GEMINI_API_KEY を読む
    client, err := genai.NewClient(ctx, &genai.ClientConfig{
        Backend: genai.BackendGeminiAPI,
    })
    if err != nil {
        log.Fatalf("client init: %v", err)
    }

    resp, err := client.Models.GenerateContent(ctx,
        "gemini-3.8-flash",
        genai.Text("Goの並行処理をエンジニア向けに3行で説明して"),
        &genai.GenerateContentConfig{Store: genai.Ptr(false)}, // リクエストをログに残さない
    )
    if err != nil {
        log.Fatalf("generate: %v", err)
    }
    fmt.Println(resp.Text())
}

第4引数の Store はv1.76.0(2026年10月22日)で追加されたフィールドです。Gemini APIのリリースノートによると、2026年10月26日から有料ティアの新規プロジェクトでは generateContent のリクエストが既定で保存されます(store=true)。既存プロジェクトは従来の設定のままです。プロジェクト所有のAPIログへの保存を止めるには、リクエスト単位で false を渡すか、AI Studioのログ設定を変更します。この設定は不正利用監視用のログとは別であり、個人情報の保持を全面的に無効化するものではありません。なお Store はGemini Developer API専用で、Vertex AI側では使えません。

Backend を書かない場合、環境変数 GOOGLE_GENAI_USE_VERTEXAI または GOOGLE_GENAI_USE_ENTERPRISE の値で接続先が決まります。両方が設定されている場合は GOOGLE_GENAI_USE_ENTERPRISE が優先され、その値が大文字・小文字を区別せず 1 または true ならVertex AI、それ以外ならGemini APIになります。ENTERPRISE側が未設定の場合だけVERTEXAI側を参照します。APIキー1本で動かすなら BackendGeminiAPI を明示しておくと、環境変数の残りで接続先が変わる事故を防げます。認証方式やSLAの違いはVertex AIとGemini APIの違いで比べています。

Gemini APIのエンドポイントURL構造

generateContent・streamGenerateContent・interactionsのURL

SDKが組み立てるURLは、ベースURL https://generativelanguage.googleapis.com/、APIバージョン(既定 v1beta)、リソースパスの3要素です。モデル名には models/ 接頭辞が付きます。下の表の「…」はベースURLを省略したものです。

用途 メソッド URL
一括生成 POST …/v1beta/models/{model}:generateContent
ストリーミング POST …/v1beta/models/{model}:streamGenerateContent?alt=sse
Interactions API POST …/v1beta/interactions
Interactions取得 GET …/v1beta/interactions/{id}

Interactions APIのパスは、公式のInteractions APIガイドのcurl例もSDK(/{api_version}/interactions)も v1beta です。2026年前半の記事には v1beta2 と書かれたものがありますが、いまのドキュメントとは一致しません。認証は x-goog-api-key ヘッダで渡します。URLに ?key= を付ける書き方は、アクセスログやリファラにキーが残るので避けてください。

net/httpだけで叩く最小クライアント

依存を増やしたくないバッチやサーバーレス関数では、標準ライブラリだけで書けます。

package main

import (
    "bytes"
    "context"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"
    "time"
)

const endpoint = "https://generativelanguage.googleapis.com/v1beta/models/gemini-3.8-flash:generateContent"

type genReq struct {
    Contents []content `json:"contents"`
}
type content struct {
    Parts []part `json:"parts"`
}
type part struct {
    Text string `json:"text"`
}
type genResp struct {
    Candidates []struct {
        Content content `json:"content"`
    } `json:"candidates"`
}

func main() {
    body, err := json.Marshal(genReq{Contents: []content{{Parts: []part{{Text: "ping"}}}}})
    if err != nil {
        log.Fatal(err)
    }

    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()

    req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(body))
    if err != nil {
        log.Fatal(err)
    }
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("x-goog-api-key", os.Getenv("GEMINI_API_KEY"))

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        log.Fatalf("gemini: status %d", resp.StatusCode)
    }
    var out genResp
    if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
        log.Fatal(err)
    }
    if len(out.Candidates) == 0 || len(out.Candidates[0].Content.Parts) == 0 {
        log.Fatal("gemini: no content (safety filter などで空になる)")
    }
    fmt.Println(out.Candidates[0].Content.Parts[0].Text)
}

最後の空チェックは省けません。安全性フィルタでブロックされた応答もHTTP 200で返り、候補が0件のケースと、候補はあるのに Parts が空のケースがあります。チェックが無いと index out of range でpanicします。

Go SDKによるInteractions APIの呼び出しと移行条件

v1.72.0での対応と会話を引き継ぐコード

Interactions APIは2026年6月に一般提供(GA)となり、公式ガイドは新規プロジェクトでの利用を推奨しています。generateContent は「legacy」と表現されるようになりましたが、引き続き完全にサポートされます。Go SDKはgo-genaiのCHANGELOGにあるとおりv1.72.0(2026年10月1日)でInteractionsを追加し、genai.Client に Interactions フィールドが入りました。それまでGoからは net/http で直接叩くしかありませんでしたが、いまはSDKで完結します。

package main

import (
    "context"
    "fmt"
    "log"

    "google.golang.org/genai"
    "google.golang.org/genai/interactions/models/interactions"
    "google.golang.org/genai/interactions/models/operations"
)

func ask(ctx context.Context, client *genai.Client, input string, prevID *string) (*interactions.Interaction, error) {
    body := operations.NewCreateInteractionRequestBody(interactions.CreateModelInteraction{
        Model:                 interactions.Model("gemini-3.8-flash"),
        Input:                 genai.Ptr(interactions.NewInteractionsInput(input)),
        PreviousInteractionID: prevID, // nil なら新しい会話
    })
    res, err := client.Interactions.Create(ctx, operations.CreateInteractionRequest{Body: body})
    if err != nil {
        return nil, err
    }
    if res.Interaction == nil || res.Interaction.ID == nil {
        return nil, fmt.Errorf("interactions: no interaction in response")
    }
    return res.Interaction, nil
}

func main() {
    ctx := context.Background()
    client, err := genai.NewClient(ctx, &genai.ClientConfig{Backend: genai.BackendGeminiAPI})
    if err != nil {
        log.Fatal(err)
    }

    first, err := ask(ctx, client, "私の名前は山田です", nil)
    if err != nil {
        log.Fatal(err)
    }
    if t := first.GetOutputText(); t != nil {
        fmt.Println(*t)
    }

    // 履歴はサーバー側が持つ。contents 配列を組み直さず ID だけ渡す
    second, err := ask(ctx, client, "私の名前は?", first.ID)
    if err != nil {
        log.Fatal(err)
    }
    if t := second.GetOutputText(); t != nil {
        fmt.Println(*t)
    }
}

Interactions側のパッケージはOpenAPIからの自動生成で、genai 本体と書き味が違います。ID・出力テキストはポインタで返るため、nilチェックが必要です。Model と Input も専用の型を通して渡します。

GenerateContentのままにする条件

Goで既存の GenerateContent から急いで移る必要はありません。移行の判断は次の3点で決まります。

  • Vertex AIで動かしている:SDK同梱のInteractionsサンプルは GOOGLE_GENAI_USE_VERTEXAI=true のとき「Interactions API is not yet supported on Vertex AI」と表示して終了します。Vertex AI経由なら GenerateContent のままです
  • Batch APIや明示的キャッシュを使っている:公式ガイドはこの2つをInteractions APIで「まだ使えない」機能として挙げています
  • 会話履歴をアプリ側のDBで管理したくない:previous_interaction_id だけで文脈が引き継がれるので、Interactionsへ移る理由になります。ただし引き継がれるのは入出力の履歴だけで、tools・system_instruction・generation_config は毎回送り直します

新規に書くコードで上の2つの制約に当たらないなら、Interactionsから始めるのが公式の推奨に沿った選択です。

モデルIDの選定と2026年11月時点の提供状況

モデルIDは文字列なので、存在しないIDを書いてもコンパイルは通り、実行時に404で落ちます。公式の廃止スケジュールとモデル一覧から、テキスト生成で使う主なIDを抜き出しました。

モデルID リリース日 状態
gemini-4-argon 2026-10-13 GA・最上位の推論モデル
gemini-3.8-flash 2026-09-02 GA・停止日未告知
gemini-3.6-flash 2026-07-21 GA・停止日未告知
gemini-3.5-flash-lite 2026-07-21 GA・停止日未告知
gemini-3.7-flash 2026-08-13 廃止・3.8 Flashへ自動転送
gemini-3.5-flash 2026-05-19 廃止・3.6 Flashへ自動転送
gemini-3.1-flash-lite 2026-05-07 停止は最短2027-05-07
gemini-2.5-flash ほか2.5系 2025-06-17〜 過去の利用者のみ
gemini-2.0-flash 2025-02-05 停止日2026-06-01を経過

古いサンプルでよく使われている gemini-2.5-flash は、廃止されてはいないものの「過去に使っていたユーザーに限定」されました。新規プロジェクトでは公式が gemini-3.8-flash か gemini-3.5-flash-lite を使うよう案内しているので、新しく書くコードの既定値はこのどちらかにします。

gemini-3.5-flash は2026年10月8日に廃止され、リクエストは gemini-3.6-flash へ自動で転送されています。エンドポイント自体は止まらないのでコードは動き続けますが、実際に応答しているのは別のモデルです。ログに記録したモデル名と実際の挙動がずれるので、文字列は書き換えておいてください。gemini-flash-latest のような latest エイリアスは新リリースのたびに中身が差し替わり、破壊的変更があるときだけ2週間前にメールで通知されます。応答の再現性が要る本番では、版を固定したIDを指定します。世代ごとの差分はGemini 3.8 Flashの解説、最上位モデルの使いどころはGemini 4 Argonの解説、単価はGemini APIの料金と無料枠にまとめています。

ストリーミング応答のrange-over-func受信

GenerateContentStream の戻り値は iter.Seq2[*genai.GenerateContentResponse, error] で、Go 1.23のrange-over-funcにそのまま渡せます。旧SDKのように iterator.Done を判定するループは不要です。

stream := client.Models.GenerateContentStream(ctx,
    "gemini-3.8-flash",
    genai.Text("Goのcontextパッケージの使いどころを箇条書きで"),
    nil,
)
for chunk, err := range stream {
    if err != nil {
        log.Fatalf("stream: %v", err)
    }
    fmt.Print(chunk.Text())
}

ループを break で抜けると、SDKはレスポンスボディを閉じます。ただし、受信を打ち切ってもサーバー側の生成停止や課金額の減少は保証されません。費用を制御するには、入力トークンの削減に加え、出力トークン上限やモデルの単価も調整します。考え方はコンテキストウィンドウのトークン上限と溢れ対策にあります。

goroutine並行呼び出しとレート制限の設計

公式のレート制限ページによると、制限はRPM・TPM(入力)・RPDの3軸で、どれか1つを超えると429が返ります。制限はAPIキー単位ではなくプロジェクト単位なので、キーを増やしても回避できません。RPDは太平洋時間の深夜0時にリセットされます。加えて10分間のローリング窓で支出上限があり、Tier 1が10ドル、Tier 2が50ドル、Tier 3が200ドルです(Freeは対象外)。支出ベースの制限が適用されるかは請求履歴やアカウントの状態によって異なります。適用対象で、高単価のモデルや大量のトークンを扱う場合は、RPMより先に支出上限へ達することがあります。

要素数だけgoroutineを起動するのではなく、同時実行数を固定します。errgroup.SetLimit を使えばワーカープールを自前で書かずに済みます(go get golang.org/x/sync が必要)。

func summarizeAll(ctx context.Context, client *genai.Client, docs []string, limit int) ([]string, error) {
    if limit <= 0 {
        return nil, fmt.Errorf("limit must be positive: %d", limit)
    }
    results := make([]string, len(docs))

    g, ctx := errgroup.WithContext(ctx)
    g.SetLimit(limit) // 同時実行数を制限する。RPMの制御には別途、時間当たりの送信数制限が必要

    for i, doc := range docs {
        g.Go(func() error {
            resp, err := client.Models.GenerateContent(ctx,
                "gemini-3.5-flash-lite",
                genai.Text("次の文書を1行で要約して:\n"+doc),
                nil,
            )
            if err != nil {
                return fmt.Errorf("doc %d: %w", i, err)
            }
            results[i] = resp.Text() // インデックス指定なので排他制御は不要
            return nil
        })
    }
    if err := g.Wait(); err != nil {
        return nil, err
    }
    return results, nil
}

クロージャ内で i・doc を直接参照できるのは、Go 1.22でループ変数がイテレーションごとに新しくなったためです。go-genaiを導入すると利用側の go ディレクティブも1.24以上に上がるため、本稿の構成ではループ変数を共有していた1.21以前の挙動は起きません。古いコードを読むときだけ注意してください。SetLimit が抑えるのは実行中のgoroutine数で、1分あたりの送信数ではありません。応答が速いとRPMを超えるので、RPMを守るには golang.org/x/time/rate の Limiter.Wait を送信の直前に挟みます。大量の要約のように即時性が要らない処理は、別枠のレート制限を持つBatch APIに回す方が429に当たりにくくなります。モデル別のRPM実数は公式ページに表が無く、AI Studioで自分のプロジェクトの値を確認する方式です。

再試行の既定値とエラー型の違い

Models系とInteractions系の既定再試行回数

go-genai v1.77.0では、同じクライアントから呼んでも系統によって再試行の既定が違います。

系統 既定の再試行 対象ステータス エラー型
Models・Chats・Files など なし(1回) 408・429・500・502・503・504 genai.APIError(値型)
Interactions 最大4回(計5回) 408・409・429・5XX *apierrors.CreateInteraction…Error(ポインタ型)

Models系の再試行処理(common.go の retryHTTPRequest)には「nil なら1回だけ試す。再試行は明示的に要求する必要があり、PythonとJavaScriptのSDKに合わせている」とコメントがあります。HTTPOptions.RetryOptions に空の構造体を渡すと、初回を含め5回・初期待機1秒・最大60秒・指数2の既定値が有効になります。Interactions側は自動生成コードに再試行が組み込まれており、初期間隔500ミリ秒・最大間隔8秒を基準とするジッター付き指数バックオフで、最大4回再試行します。Retry-Afterヘッダがあればその待機時間を優先します。設定構造体には MaxElapsedTime(30秒)もありますが、既定の attempt-count-backoff 経路はこれを参照しないので、処理全体の期限は context で切ってください。

httptest で常に429を返すサーバーを立てて呼ぶと、届いたリクエストはModels既定で1回、RetryOptions{} 指定で5回、Interactions既定で5回でした。手書きの再試行ループをInteractionsに重ねると、最悪で5×自前回数のリクエストが飛びます。Models系はSDKの RetryOptions で再試行し、Interactions系は自前で重ねない、と分けておくのが安全です。

errors.Asによる値型・ポインタ型エラーの判定

Models系のエラーは genai.APIError が値型で返ります。var e *genai.APIError で受けると errors.As は常に false になり、ステータス判定が丸ごと無効になります。一方、Interactions系は *apierrors.CreateInteractionClientError(4xx)と *apierrors.CreateInteractionServerError(5xx)のポインタ型で、こちらは genai.APIError では捕まりません。両方を扱う関数を用意しておくと、呼び出し側の分岐が1か所で済みます。

func newClient(ctx context.Context) (*genai.Client, error) {
    return genai.NewClient(ctx, &genai.ClientConfig{
        Backend: genai.BackendGeminiAPI,
        HTTPOptions: genai.HTTPOptions{
            // 未指定のままだと Models 系は再試行しない。空の構造体で既定値(計5回)が有効になる
            RetryOptions: &genai.HTTPRetryOptions{},
        },
    })
}

// statusOf は Models 系と Interactions 系のエラーから HTTP ステータスを取り出す
func statusOf(err error) (int, bool) {
    // Models / Chats / Files などは値型の genai.APIError
    var apiErr genai.APIError
    if errors.As(err, &apiErr) {
        return apiErr.Code, true
    }
    // Interactions はポインタ型。4xx と 5xx で型が分かれる
    var clientErr *apierrors.CreateInteractionClientError
    if errors.As(err, &clientErr) && clientErr.HTTPMeta.Response != nil {
        return clientErr.HTTPMeta.Response.StatusCode, true
    }
    var serverErr *apierrors.CreateInteractionServerError
    if errors.As(err, &serverErr) && serverErr.HTTPMeta.Response != nil {
        return serverErr.HTTPMeta.Response.StatusCode, true
    }
    return 0, false
}

import には google.golang.org/genai/interactions/models/apierrors を追加します。400の入力エラーや403の権限エラーは何度送っても結果が変わらないので、再試行対象から外して呼び出し元へ返します。503が長引くときの切り分けは503エラーの原因切り分けと復旧を参照してください。社内システムとの連携まで含めた設計を外部に任せる場合は、API開発・システム連携の支援も用意しています。

よくある質問

Gemini APIのエンドポイントURLは何ですか?

テキスト生成は https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent です。ストリーミングは末尾を :streamGenerateContent?alt=sse に、Interactions APIは /v1beta/interactions にします。APIキーは x-goog-api-key ヘッダで渡します。

Go SDKはInteractions APIに対応していますか?

対応しています。google.golang.org/genai のv1.72.0(2026年10月1日)で追加され、client.Interactions.Create で呼べます。ただし同梱サンプルではVertex AI経由は未対応とされているので、Vertex AIで動かす場合は GenerateContent を使います。

gemini-3.5-flashの提供終了日はいつですか?

2026年10月8日に廃止され、Google AI Studioでは選べなくなりました。APIのエンドポイントは止まらず、リクエストは gemini-3.6-flash へ自動で転送されています。コードはそのまま動きますが、モデル名は gemini-3.6-flash か gemini-3.8-flash に書き換えてください。

generative-ai-goはまだ使えますか?

モジュールは公開されたままなので go get は通りますが、サポートは2025年11月30日で終了し、リポジトリもアーカイブされています。新しいモデルや機能は入らないので、google.golang.org/genai へ移行してください。

Goで指定するモデルIDはどれを選べばよいですか?

新規プロジェクトなら gemini-3.8-flash、大量処理でコストを抑えるなら gemini-3.5-flash-lite です。gemini-2.5-flash は過去の利用者に限定されているため、新しいプロジェクトでは使えません。複雑な推論が要る処理だけ gemini-4-argon に振り分ける構成も取れます。

関連記事

お気に入りに入れた記事の一覧

一創の公式 SNS

X では毎朝の新着記事と IT ニュースの速報を、YouTube・TikTok・Instagram・LinkedIn・Facebook では記事の解説動画を配信しています。

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.11.03 テックブログ ノジマ不正アクセスで約700万件漏えい:対象確認・窓口と乗っ取り注文を止める実装
  2. 2026.10.29 テックブログ ソニー生命の情報漏洩38.5万件|ログインできない理由、再開状況と窓口の電話番号
  3. 2026.11.03 テックブログ クラブツーリズム不正アクセス:約432万件の対象確認と偽メールの見分け方・流出検知の設計
  4. 2026.11.02 テックブログ アララの不正アクセス:メール約7,700万件の持ち出しと契約企業が行うAPIキー再設定・代替送信・連名報告
  5. 2026.11.02 テックブログ サントリーの不正アクセスで約10万人分漏えい|M365のファイル大量ダウンロードを監査ログで調べる手順

RELATED POSTS 関連記事

目次