個人開発のアプリに Gemini API を組み込んだ初日、私自身がいちばん時間を取られたのは、エラーコードそのものよりも「これは待てば直るのか、それともコードを直すべきなのか」という判断でした。429 が返ってきても、レート制限なのか認証なのか、レスポンスの status を読むまでは分かりません。
ここでは、Gemini API で遭遇するエラーを status と finish_reason で分類し、本番で止まらないように処理へ落とし込む方法を、実際に使っているコードとともに整理します。狙いはエラーの一覧をつくることではなく、出たときに迷わず手が動く状態をつくることです。
Gemini APIでよくあるエラーの種類
Gemini APIを利用していると、以下のようなエラーが発生することがあります。
400 Bad Request — リクエストの形式が不正
401/403 Unauthorized/Forbidden — 認証エラー
429 Too Many Requests — レート制限に達した
500/503 Internal Server Error — Google側の一時的な障害
これらのエラーは、原因がわかれば簡単に解決できるものばかりです。それぞれの対処法を見ていきましょう。
400 Bad Request — リクエスト形式・モデル名の誤り
400 Bad Requestエラーは、サーバーがリクエストを理解できない場合に発生します。主な原因は3つです。
1. モデル名の誤り
最も多いのはモデル名を間違えるケースです。Gemini APIで利用できるモデルは限られています。
import google.generativeai as genai
genai.configure( api_key = "YOUR_GEMINI_API_KEY" )
# ❌ 間違い:モデル名が存在しない
model = genai.GenerativeModel( "gemini-invalid-model" )
# ✅ 正解:実在するモデル名を使う
model = genai.GenerativeModel( "gemini-2.0-flash" )
response = model.generate_content( "こんにちは" )
print (response.text)
最新で利用可能なモデル一覧は、Google AI Studioの「モデルの選択」ページ で確認できます。よく使われるモデルは以下の通りです:
gemini-2.0-flash — 高速・軽量
gemini-2.0-pro — より高精度
gemini-2.0-flash-thinking-exp-01-21 — 思考拡張モデル
2. JSONフォーマットが不正
TypeScript/JavaScriptでAPIを呼び出す場合、リクエストボディのJSON形式が正しくないと400エラーが発生します。
import { GoogleGenerativeAI } from "@google/generative-ai" ;
const genAI = new GoogleGenerativeAI ( "YOUR_GEMINI_API_KEY" );
const model = genAI. getGenerativeModel ({ model: "gemini-2.0-flash" });
// ✅ 正しいリクエスト形式
const response = await model. generateContent ({
contents: [
{
role: "user" ,
parts: [{ text: "こんにちは" }],
},
],
});
特にcontents、parts、roleなどの必須フィールドが欠落していないか確認してください。
3. 入力長が上限を超えている
テキスト入力が非常に長い場合、リクエストが400エラーで拒否されることがあります。Gemini APIのコンテキストウィンドウ(入力可能な最大文字数)は、モデルごとに異なります。
gemini-2.0-flash — 最大100万トークン
もし大量のテキストを処理する必要があれば、テキストを分割して複数回に分けてリクエストを送るようにしてください。
401/403 認証エラー — APIキーの設定方法
401(Unauthorized)または403(Forbidden)エラーは、APIキーが設定されていないか、権限がない場合に発生します。
Google AI Studioを使う場合
Google AI Studioはもっとも簡単にGemini APIを試す方法です。
Google AI Studio にアクセス
左側の「API キー」メニューから「新しいAPIキーを作成」をクリック
表示されたAPIキーをコピー
コード内で以下のように設定:
import google.generativeai as genai
# 方法1: 直接設定(開発用のみ推奨)
genai.configure( api_key = "YOUR_GEMINI_API_KEY" )
# 方法2: 環境変数から読み込み(本番環境推奨)
import os
api_key = os.getenv( "GEMINI_API_KEY" )
genai.configure( api_key = api_key)
環境変数に設定する場合は、Linux/macOSでは以下のコマンドを実行します:
export GEMINI_API_KEY = "your_actual_api_key"
Vertex AIを使う場合
Google Cloudプロジェクトを使用する場合は、Vertex AIを使う必要があります。Google AI Studioとは異なるセットアップが必要です。
import google.generativeai as genai
from google.auth import default
# GCPの認証を自動取得
credentials, project_id = default()
genai.configure( credentials = credentials)
Vertex AIを初めて使う場合は、GCPプロジェクトでVertex AI APIを有効にする必要があります。詳しくはVertex AIのドキュメント をご参照ください。
429 Too Many Requests — レート制限の理解と回避策
429エラーは、短時間に大量のリクエストを送った場合に発生します。Gemini APIには、無料枠と有料枠でそれぞれ異なるレート制限があります。
無料枠のレート制限
Google AI Studioで無料でGemini APIを使う場合、以下の制限があります:
1分あたり最大60リクエスト
1日あたりの総リクエスト数に上限あり
有料枠のレート制限
Google Cloudで有料契約した場合:
1分あたり最大360リクエスト(通常)
利用プランやモデルによって異なる
指数バックオフでの対処
429エラーが発生した場合、すぐにリトライするのではなく、待機時間を段階的に増やして再試行する「指数バックオフ」を使うのが定石です。
import time
import google.generativeai as genai
genai.configure( api_key = "YOUR_GEMINI_API_KEY" )
model = genai.GenerativeModel( "gemini-2.0-flash" )
def generate_with_retry (prompt, max_retries = 5 ):
"""指数バックオフを使ったリトライロジック"""
for attempt in range (max_retries):
try :
response = model.generate_content(prompt)
return response.text
except Exception as e:
if "429" in str (e):
# 待機時間: 1秒、2秒、4秒、8秒、16秒...
wait_time = 2 ** attempt
print ( f "レート制限に達しました。 { wait_time } 秒待機します..." )
time.sleep(wait_time)
else :
raise
# 使用例
result = generate_with_retry( "日本の首都は?" )
print (result)
TypeScript/JavaScriptの場合:
import { GoogleGenerativeAI } from "@google/generative-ai" ;
const genAI = new GoogleGenerativeAI ( "YOUR_GEMINI_API_KEY" );
const model = genAI. getGenerativeModel ({ model: "gemini-2.0-flash" });
async function generateWithRetry ( prompt : string , maxRetries = 5 ) : Promise < string > {
for ( let attempt = 0 ; attempt < maxRetries; attempt ++ ) {
try {
const response = await model. generateContent (prompt);
return response.response. text ();
} catch (error) {
if (error instanceof Error && error.message. includes ( "429" )) {
const waitTime = Math. pow ( 2 , attempt) * 1000 ; // ミリ秒に変換
console. log ( `レート制限に達しました。${ waitTime / 1000 }秒待機します...` );
await new Promise (( resolve ) => setTimeout (resolve, waitTime));
} else {
throw error;
}
}
}
throw new Error ( "最大リトライ回数に達しました" );
}
// 使用例
const result = await generateWithRetry ( "日本の首都は?" );
console. log (result);
どの上限に当たっているのか — RPM・RPD・TPM
429 は単一の原因ではありません。Gemini API では3種類の上限が独立して働いています。
RPM(Requests Per Minute)— 1分あたりのリクエスト数
RPD(Requests Per Day)— 1日あたりのリクエスト数
TPM(Tokens Per Minute)— 1分あたりに処理できるトークン数
個人開発のアプリで私自身が最初に引っかかったのは TPM でした。リクエスト数は少なくても、長いプロンプトを連続して送ると、件数の上限より先にトークンの上限へ達します。まずはどの上限に当たっているのかを切り分けることが、対処の出発点になります。
RESOURCE_EXHAUSTED は本当にクォータ超過か
429 のレスポンス本文には、次のように RESOURCE_EXHAUSTED が入ります。
{
"error" : {
"code" : 429 ,
"message" : "Resource has been exhausted (e.g. check quota)." ,
"status" : "RESOURCE_EXHAUSTED"
}
}
RESOURCE_EXHAUSTED はクォータ超過のサインで、時間をおけば回復します。一方で INVALID_ARGUMENT や PERMISSION_DENIED はリクエスト形式や認証の問題です。待っても直りませんので、status を確認して「待つべきか、直すべきか」を最初に見極めてください。
無料枠と有料枠でクォータはどれくらい違うか
無料枠は開発と検証のための枠で、本番運用は想定されていません。おおよその目安は次のとおりです(具体的な数値はモデルやリージョン、時期で変わりますので、最新値は公式ドキュメントでご確認ください)。
無料枠 — RPM はおよそ数十件/分、RPD は1日あたり数千件、TPM は1万トークン前後、同時接続は2程度
有料枠(従量課金)— RPM は数千件/分まで、RPD は実質的な上限なし、TPM は10万〜100万トークン、同時接続は100程度
見落としやすいのは、これらの上限がユーザー単位ではなくプロジェクト単位で共有される点です。同じプロジェクトで検証用スクリプトと本番アプリが同時に API を叩くと、合算で上限に達します。私自身、テスト用のバッチを流したまま本番アプリが 429 になり、原因の特定に時間を取られたことがありました。
規模の目安としては、月1万リクエスト未満なら無料枠と上限設定で十分、月100万リクエストまでは従量課金、それ以上は Google Cloud のボリューム相談が現実的です。
クォータの確認と引き上げ
現在の使用状況は、Google Cloud Console から確認できます。
Google AI Studio で対象のプロジェクトを確認します
Google Cloud Console で「API とサービス」から Generative Language API を開き、「クォータと使用」を確認します
「請求」で支払い情報を設定すると、有料枠の上限へ自動的に切り替わります(反映まで数分〜数時間かかることがあります)
予期しない請求を防ぐ
有料枠に切り替える前に、予算アラートを設定しておくと安心です。Google Cloud Console の「請求」から「予算」を開いて月額上限を決め、「予算の100%で通知」などのアラートを有効にしておきます。上限到達時に API 呼び出しを止める運用にしておけば、想定外の高額請求を避けられます。
使用率を監視する
本番では、上限に達してから気づくのでは手遅れです。Cloud Monitoring で quota_used_count を監視し、使用率が80%を超えたら通知するようにしておくと、有料枠への切り替えやリトライ設計を前もって判断できます。
displayName : Gemini API quota alert
conditions :
- displayName : RPM usage > 80%
conditionThreshold :
filter : |
resource.type="api"
AND metric.type="serviceruntime.googleapis.com/api/consumer/quota_used_count"
AND resource.service="generativelanguage.googleapis.com"
threshold_value : 0.8
comparison : COMPARISON_GT
500/503 サーバーエラー — Google側の一時的な障害
500(Internal Server Error)または503(Service Unavailable)エラーは、Google側のシステムに一時的な問題が発生している場合に起こります。これはあなたのコードやAPIキーの設定に問題があるわけではありません。
対応方法
少し待機してリトライする(指数バックオフを使用)
Google Cloud Status Dashboard(status.cloud.google.com )でGemini APIのステータスを確認
問題が継続する場合は、Google Cloud Supportに連絡する
import time
def generate_with_500_retry (prompt, max_retries = 3 ):
"""500/503エラーに対応したリトライロジック"""
for attempt in range (max_retries):
try :
response = model.generate_content(prompt)
return response.text
except Exception as e:
error_code = str (e)
if "500" in error_code or "503" in error_code:
wait_time = 5 * (attempt + 1 ) # 5秒、10秒、15秒
print ( f "サーバーエラーが発生しました。 { wait_time } 秒後に再試行します..." )
time.sleep(wait_time)
else :
raise
コンテキスト長の超過 — INVALID_ARGUMENT とトークン上限の対処
長いドキュメントや複数の画像をまとめて渡したとき、次のような400系エラーに出会うことがあります。
google.api_core.exceptions.InvalidArgument: 400 Request payload size exceeds the limit
マルチモーダルのリクエストでは、こう出ることもあります。
Invalid request: the total input token count exceeds the model's limit
入力がモデルのコンテキストウィンドウを超えると発生します。エラーを受けてから慌てるより、送信前にトークン数を数えておくと安全です。
import google.generativeai as genai
import os
genai.configure( api_key = os.environ[ "GOOGLE_API_KEY" ])
model = genai.GenerativeModel( "gemini-2.0-flash" )
def count_tokens (prompt: str ) -> int :
# 送信前にトークン数を確認する
return model.count_tokens(prompt).total_tokens
def safe_generate (prompt: str , max_chars: int = 30000 ) -> str :
if len (prompt) > max_chars:
# 末尾を切るより中間を省く方が、前後の文脈が保たれます
half = max_chars // 2
prompt = prompt[:half] + " \n ...[中略]... \n " + prompt[ - half:]
return model.generate_content(prompt).text
末尾だけを切り落とすと、肝心の結論が失われがちです。先頭と末尾を残して中間を省くと、指示と締めくくりの両方が手元に残ります。
長文を丸ごと扱いたいときは、Gemini 2.5 Pro や Flash の100万トークンコンテキストに切り替えるか、ドキュメントを意味のまとまりで分割して順に渡す方法を検討してみてください。
応答が空・ブロックされる — finish_reason で原因を切り分ける
エラーらしいエラーは出ないのに、テキストだけ取れない。これも実際によくつまずく場面です。
response = model.generate_content( "..." )
print (response.text) # AttributeError: 'NoneType' object has no attribute 'text'
response.text にいきなり触れず、まず candidates の有無と finish_reason を確認します。原因によって打つ手が変わるからです。
def safe_get_text (response) -> str | None :
# candidates が空なら入力段階でブロックされている
if not response.candidates:
print ( f "入力がブロックされました: { response.prompt_feedback } " )
return None
candidate = response.candidates[ 0 ]
reason = candidate.finish_reason.name
if reason == "SAFETY" :
print ( f "安全フィルターでブロック: { candidate.safety_ratings } " )
return None
if reason == "RECITATION" :
print ( "著作権コンテンツの検出によりブロック" )
return None
if reason == "MAX_TOKENS" :
# 途中までのテキストは取り出せる
print ( "max_output_tokens に達しました。上限を増やしてください" )
try :
return candidate.content.parts[ 0 ].text
except ( IndexError , AttributeError ):
return None
SAFETY ならプロンプトを中立的な表現に直します。RECITATION は既存の文章をそのまま再生成しようとしたときに出るので、要約や言い換えを求める形に変えます。MAX_TOKENS は max_output_tokens を引き上げます。原因ごとに対処が決まっていれば、空応答に振り回されずに済みます。
安全フィルターの感度は safety_settings で調整できますが、まずはプロンプト側の言い回しを見直すだけで解決することがほとんどです。入力プロンプトの内容を見直すところから始めてみてください。
すべての例外を1か所で分類する統一ハンドラ
エラーごとに try/except を書き散らすと、コードベースが大きくなるほど対処の抜け漏れが生まれます。私自身、復旧用のバッチを日常的に回すようになってからは、API 呼び出しを1つのラッパーに通し、status と finish_reason での分岐を一元化する形に落ち着きました。
考え方はシンプルです。例外を「待てば直る(リトライ)」「直さない限り直らない(即失敗)」「部分的に使える(警告して継続)」の3群に分けるだけです。
import time
import random
import google.generativeai as genai
from google.api_core import exceptions as gax
genai.configure( api_key = "YOUR_GEMINI_API_KEY" )
model = genai.GenerativeModel( "gemini-2.0-flash" )
# 待てば回復する見込みのある状態
RETRYABLE = (gax.ResourceExhausted, gax.ServiceUnavailable, gax.InternalServerError)
# 直さない限り回復しない状態
FATAL = (gax.InvalidArgument, gax.PermissionDenied, gax.Unauthenticated)
def call_gemini (prompt, max_retries = 5 , base = 1.0 ):
for attempt in range (max_retries):
try :
return model.generate_content(prompt)
except RETRYABLE as e:
# 指数バックオフ + ジッター(同時リトライの集中を避ける)
wait = base * ( 2 ** attempt) + random.uniform( 0 , base)
print ( f "[retry { attempt + 1 } ] { type (e). __name__ } : { wait :.1f } s 待機" )
time.sleep(wait)
except FATAL as e:
# ここで待つのは時間の無駄。呼び出し側へ即返す
raise RuntimeError ( f "修正が必要なエラー: { type (e). __name__ } : { e } " ) from e
raise RuntimeError ( "最大リトライ回数を超過しました" )
ジッター(random.uniform)を足しているのには理由があります。複数のワーカーが同時に 429 を受けると、固定の待機時間ではリトライのタイミングがそろってしまい、回復した瞬間にまた一斉にぶつかります。少しのゆらぎを入れるだけで、この再衝突が目に見えて減りました。
status と対処の対応表
迷ったときに立ち返れるよう、status と打つべき手を一枚にまとめておきます。
status / 症状 意味 打つ手 リトライ
RESOURCE_EXHAUSTED (429) RPM / RPD / TPM のいずれか超過 バックオフ + ジッター、上限の引き上げ する
INVALID_ARGUMENT (400) モデル名・JSON・入力長の誤り リクエストを修正する しない
UNAUTHENTICATED (401) APIキー未設定・無効 キーと環境変数を確認する しない
PERMISSION_DENIED (403) API未有効・権限不足 プロジェクト設定を確認する しない
INTERNAL / UNAVAILABLE (500/503) Google側の一時障害 待ってリトライ、ステータス確認 する
finish_reason=SAFETY 安全フィルターで遮断 プロンプトを中立な表現へ しない
finish_reason=MAX_TOKENS 出力上限に到達 max_output_tokens を引き上げ 条件付き
この表で効いてくるのは右端の「リトライ」列です。待っても無駄な FATAL 系をリトライし続けると、ユーザーを待たせたうえに結局失敗します。最初に「待つ価値があるか」を判定するだけで、体感のレスポンスが変わります。私はこの判定を、新しいプロジェクトを立ち上げるときも、いちばん最初に入れるようにしています。
その他のよくあるエラー
モデルが廃止されている
古い記事やコードを参照している場合、すでに廃止されたモデルを使おうとしている可能性があります。gemini-1.5-proやgemini-1.5-flashは、より新しいgemini-2.0-proやgemini-2.0-flashに置き換わっています。
リージョン制限エラー
一部のモデルやAPIは、特定の地域でしか利用できない場合があります。Vertex AIを使う場合は、プロジェクトと同じリージョンでAPIを呼び出すようにしてください。
トラブルシューティングのコツ
1. エラーメッセージを丁寧に読む
エラーメッセージには、問題の原因に関する情報が含まれています。以下の情報を確認しましょう:
エラーコード(400・401・429など)
エラーメッセージ本文
発生時刻
2. APIドキュメントを参照
Gemini APIの公式ドキュメントには、各エラーについて詳しい説明が書かれています。わからないことがあったら、ドキュメントを参照することをお勧めします。
3. ローカルでテストする
本番環境でデバッグするのではなく、まずはローカル環境で小さなスクリプトを使ってテストしましょう。
4. ログを記録する
APIの呼び出しをログに記録することで、問題が発生した時の原因追跡がしやすくなります。
import logging
logging.basicConfig( level = logging. DEBUG )
logger = logging.getLogger( __name__ )
try :
response = model.generate_content( "テスト" )
logger.info( f "成功: { response.text } " )
except Exception as e:
logger.error( f "エラーが発生しました: { e } " )
まずやるべきこと
エラーが出たときの切り分け手順として、以下の順番で確認してみてください。
GOOGLE_API_KEYが正しく設定されているか(echo $GOOGLE_API_KEYで確認)
モデル名が公式ドキュメントと一致しているか
response.candidatesが空でないか確認してからテキスト取得する
レート制限には指数バックオフを実装する
これだけで、Gemini API開発でつまずきやすい場面の大半はカバーできます。
次の一歩
エラー対処の要点は、一覧を覚えることではなく、「出たときに status と finish_reason を見て、待つか直すかを即決できること」に尽きます。まずは今お使いのコードの API 呼び出しを、上で示した統一ハンドラ1つに通すところから始めてみてください。分岐が1か所にまとまっていれば、新しいエラーに出会ったときも対処の追加が一行で済みます。
困ったときの公式リソースも添えておきます。
同じところでつまずいた方の時間を、少しでも短くできれば嬉しいです。