新しいプロジェクトで API キーを作ると、画面には確かに Free tier と出ています。サンプルコードを貼って実行すると、最初の 1 回目から 429 が返ってくる——そんな相談を、開発者フォーラムで繰り返し見かけます。
こういうとき、多くの方がまず「リクエストを送りすぎたのだろう」と考えて待ちます。けれど 1 回目から出ている 429 は、待っても消えません。原因が「使い切った」ではなく「最初から枠がない」ところにあるからです。
最初にお伝えしたいのは、エラー本文に書かれている limit: 0 の読み方です。ここが読めると、あとの確認はほとんど機械的に進みます。
limit: 0 は「使いすぎ」ではなく「枠がゼロ」という意味です
通常の 429 は、上限が 10 で 11 回目を送った、というように、決まった枠を超えたときに返ります。一方 limit: 0 は、そのモデル・そのプロジェクトの組み合わせに、そもそも割り当てが無いことを示しています。
つまり直す対象は、送る頻度ではありません。「どのモデルを」「どのプロジェクトで」「どの条件で」呼んでいるかのほうです。レート制限は、公式のレート制限のページにあるとおり、モデルごと・プロジェクトごとに決まります。同じキーでもモデルを変えると通る、ということが起こり得るのはこのためです。
画面の Free tier という表示は、プロジェクトの区分を示しているだけで、すべてのモデルが無料で使えるという約束ではありません。ここを取り違えると、設定をいじり回す時間が長くなってしまうかもしれません。
最初の 1 時間に見る順番(5 段階)
闇雲に設定を触る前に、確認の順番を決めておきます。私は次の順で見ています。上ほど短時間で白黒がつき、下ほど時間がかかるからです。
| 順 | 確認すること | 白黒のつけ方 |
|---|---|---|
| 1 | 呼んでいるモデル名 | 別のモデル名で 1 回だけ試す。通れば、そのモデルに枠がないだけです |
| 2 | キーとプロジェクトの対応 | 画面で見ているプロジェクトと、コードで使っているキーの持ち主が同じか確かめます |
| 3 | エラー本文の `quotaMetric` と `quotaValue` | どの指標が 0 なのかを読みます(次の章で自動化します) |
| 4 | 提供地域と課金設定 | 公式の[課金のページ](https://ai.google.dev/gemini-api/docs/billing)と、無料枠が使える地域の案内を照らします |
| 5 | 設定直後の反映待ち | 課金を紐づけた直後などは、少し時間を置いて同じ呼び出しを再実行します |
1 と 2 は数分で終わります。3 までで原因が絞れることが多く、4 と 5 は、3 の結果を見てから進めば十分です。順番を守るだけで、「設定画面を何周もする」時間をかなり減らせます。
40 行のスクリプトで、モデルごとに切り分けます
手で 1 つずつ試すのは面倒ですし、エラー本文は長くて読みにくいものです。そこで、キーで見えているモデルのうち数個に 1 回ずつ最小のリクエストを送り、エラー本文から quotaMetric と quotaValue だけを取り出して並べるスクリプトを用意しました。標準ライブラリだけで動きます。
import json, os, re, sys, urllib.request, urllib.error
KEY = os.environ["GEMINI_API_KEY"] # 実際のキーはコードに書かず環境変数から
BASE = "https://generativelanguage.googleapis.com/v1beta"
def call(path, body=None):
data = json.dumps(body).encode() if body else None
req = urllib.request.Request(
f"{BASE}/{path}", data=data,
headers={"x-goog-api-key": KEY, "Content-Type": "application/json"})
try:
with urllib.request.urlopen(req, timeout=30) as r:
return r.status, json.load(r)
except urllib.error.HTTPError as e:
return e.code, json.loads(e.read() or b"{}")
# 1) このキーで見えているモデルを一覧する(クォータを消費しません)
_, listing = call("models")
names = [m["name"] for m in listing.get("models", [])
if "generateContent" in m.get("supportedGenerationMethods", [])]
targets = sys.argv[1:] or [n for n in names if "flash" in n][:3]
print("見えているモデル数:", len(names))
# 2) 各モデルに最小のリクエストを 1 回だけ送る
for name in targets:
name = name if name.startswith("models/") else f"models/{name}"
code, res = call(f"{name}:generateContent",
{"contents": [{"parts": [{"text": "ping"}]}],
"generationConfig": {"maxOutputTokens": 8}})
if code == 200:
print(f"OK {name}")
continue
err = res.get("error", {})
zero = bool(re.search(r"limit:\s*0", err.get("message", "")))
for d in err.get("details", []):
for v in d.get("violations", []):
zero = zero or str(v.get("quotaValue")) == "0"
print(f" metric={v.get('quotaMetric')} value={v.get('quotaValue')}")
kind = "枠がゼロ" if zero else "使い切り or 別の原因"
print(f"{code} {name} → {kind}")前に置いた一覧の呼び出し(models)は、生成を伴わないのでクォータを消費しません。ここでモデルが 1 つも見えなければ、そもそもキーかプロジェクトの側を疑う、という分岐も得られます。
最小のリクエストにしているのは、失敗した原因が「中身」ではなく「枠」にあると一度で確かめたいからです。maxOutputTokens を小さくしておけば、通った場合の消費もほんの僅かです。
使い方は、環境変数にキーを入れて python3 probe.py と実行するだけです。普段使っているモデル名を引数に渡すと、そのモデルだけを試します。
出力の読み方
実行結果は、おおむね次の 3 つの形に分かれます。
| 出力 | 意味 | 次の一手 |
|---|---|---|
| 一部のモデルが OK、他は「枠がゼロ」 | そのモデルに、このプロジェクトでは枠がありません | OK のモデルへ切り替えて先に進み、必要なら課金設定を確認します |
| すべてのモデルが「枠がゼロ」 | プロジェクト、地域、課金のいずれかが原因です | 確認順の 2 と 4 に進みます |
| 「使い切り or 別の原因」 | 枠はあるので、通常のレート制限の可能性が高いです | 間隔を空けて再試行します。設計は[Gemini API の 429 を全部リトライしてはいけません — レート制限と Spend Cap 枯渇を見分けるリトライ設計](/articles/gemini-api/gemini-api-429-retryable-vs-spend-cap-exhaustion-retry-design)にまとめています |
一番上の形は、拍子抜けするほど単純です。「このモデルは使えなかっただけ」と分かって、その場で動くようになることもあるのだと思います。
直らないときは、貼る情報をそろえてから聞きます
ここまでで原因が絞れない場合は、フォーラムやサポートに質問することになります。そのとき、次の 5 つが揃っていると、返事が早く、的確になります。
- 呼んだモデル名
- エラー本文の
quotaMetricとquotaValue - 発生した日時(タイムゾーンつき)
- プロジェクトの識別子(キー自体は絶対に貼りません)
- 課金を設定したかどうかと、その日時
同じ症状の相談はフォーラムのスレッドにも上がっており、状況がそろった書き込みほど原因の見当がつきやすい印象を受けます。
結びに
今回の要点を一文にまとめるなら、こうなります。
1 回目から出る 429 は、待つのではなく、枠の有無を読みにいきます。
次の一歩は小さく、今日のうちにできます。上のスクリプトを 1 回だけ実行し、あなたのプロジェクトで「枠がゼロ」のモデルがどれかを書き留めてみてください。その一覧が手元にあるだけで、次に同じ画面を見たときの落ち着きがまるで違ってきます。
個人開発で試作用のプロジェクトを立てるたびに、私は最初にこの確認を入れるようにしております。