課金される前にトークンを数える
Gemini API の請求は、やり取りしたトークン数で決まります。やっかいなのは、プロンプトに長いコンテキストや画像を載せると、思っていたより一気にトークンが膨らむことです。私は壁紙アプリのレビューを Gemini で分類する小さな機能を運用していますが、最初に試作したときは、レビュー本文に商品説明やテンプレ文を丸ごと添付してしまい、1件あたりの入力トークンが想定の3倍に膨れていました。
ありがたいことに、Gemini には送信前にトークン数を測る count_tokens が用意されています。しかもこの呼び出し自体は無料です。実際に課金されるリクエストを投げる前に、入力がどれくらいのトークンを消費するのかを確かめられます。まずはこれを習慣にするだけで、コストの当たりは大きく外れなくなります。
from google import genai
client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
response = client.models.count_tokens(
model="gemini-2.5-flash",
contents="Gemini API でアプリのレビューを感情分類したいです。",
)
print(f"入力トークン数: {response.total_tokens}")
# 入力トークン数: 18count_tokens が返すのは入力側のトークン数です。出力トークンは生成してみるまで確定しませんが、max_output_tokens で上限を決めておけば、出力側の最悪値も先に見積もれます。
トークンという単位の感覚をつかむ
トークンは言語モデルが処理する最小単位です。1トークンはおおよそ英語4文字に相当しますが、言語や記号で大きく変わります。日次の概算を素早く立てるときは、次の目安が役立ちます。
英語の散文 : 約 1 単語 = 1.3 トークン
日本語の文 : 約 1 文字 = 1 トークン
ソースコード : 約 1 行 = 10〜20 トークン
画像 : 解像度に応じて 数百〜2,000 トークン前後
この感覚があると、「このプロンプトはざっくり何トークンか」を暗算でき、count_tokens の結果が桁違いに出たときに「何かを余分に渡している」と気づけます。私の場合、レビュー分類で入力が膨らんだ原因は、毎回同じ分類ルール(数千文字)をユーザーメッセージ側に貼り付けていたことでした。これは後述するキャッシュで解決できます。
テキストと画像のトークンを数える
Gemini はテキストだけでなく画像や動画も扱えます。マルチモーダルのリクエストでは画像が入力トークンの大半を占めることも珍しくないので、テキスト単体のときより一段慎重に測っておきます。画像は types.Part.from_bytes でそのまま contents に混ぜられます。
import pathlib
from google import genai
from google.genai import types
client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
image_bytes = pathlib.Path("screenshot.png").read_bytes()
response = client.models.count_tokens(
model="gemini-2.5-flash",
contents=[
types.Part.from_bytes(data=image_bytes, mime_type="image/png"),
"このスクリーンショットの主要な要素を3つ挙げてください。",
],
)
print(f"テキスト+画像の入力トークン: {response.total_tokens}")画像のトークンは解像度に依存します。同じ被写体でも高解像度のまま送ると数倍に増えるため、分類や要約のように細部が要らない用途では、送信前に長辺を 1,024px 程度へリサイズするだけで入力トークンを実測で半分以下に抑えられました。
月額コストを概算する計算機
トークン数が読めるようになったら、料金表と掛け合わせて月額を概算します。料金は改定されるので、最新値は必ず公式の料金ページで確認し、コードのなかは一箇所にまとめておくのが安全です。下記は 2026年6月時点のおおよその単価(100万トークンあたり・米ドル)を入れた例です。
class GeminiCostCalculator:
"""Gemini API のコストを概算する。単価は要・最新確認。"""
# 100万トークンあたりの単価 (USD) — 2026年6月時点の目安
PRICING = {
"gemini-2.5-flash": {"input": 0.30, "output": 2.50},
"gemini-2.5-pro": {"input": 1.25, "output": 10.00}, # 200k超は input/output が上振れ
}
@classmethod
def request_cost(cls, input_tokens, output_tokens, model="gemini-2.5-flash"):
if model not in cls.PRICING:
raise ValueError(f"未知のモデル: {model}")
p = cls.PRICING[model]
input_cost = input_tokens / 1_000_000 * p["input"]
output_cost = output_tokens / 1_000_000 * p["output"]
return round(input_cost + output_cost, 6)
@classmethod
def monthly_cost(cls, daily_requests, avg_in, avg_out, model="gemini-2.5-flash"):
per_day = cls.request_cost(avg_in, avg_out, model) * daily_requests
return round(per_day * 30, 2)
# 1日 1,000 件、平均 入力500 / 出力300 トークンを Flash で処理した場合
monthly = GeminiCostCalculator.monthly_cost(1000, 500, 300, "gemini-2.5-flash")
print(f"月額の概算: ${monthly}")ここで効いてくるのがモデル選択です。同じ処理を Pro と Flash で見積もると、入力単価で 4倍前後、出力単価ではさらに開きます。レビュー分類のように判断が単純な処理は Flash で十分で、私はこの一本化だけで月のAPI費を体感で半分以下にできました。複雑な推論や長文生成だけ Pro に回す、という振り分けが現実的です。個人開発でアプリを出し続けてきた身としては、2014年の頃から広告(AdMob)収益とサーバー費の差が利益を決める、という感覚が染みついています。だからこそ API 費のような変動コストは、できるだけ送信前に読み切っておきたいのです。
コストを下げる3つの実践
1. 繰り返すコンテキストはキャッシュに逃がす
同じ指示文や長い参照ドキュメントを毎回プロンプトに貼ると、その分だけ入力トークンを毎回払うことになります。Gemini のコンテキストキャッシュを使うと、再利用部分のトークンを割引価格で読めます。2.5 系では自動で効く暗黙キャッシュもありますが、確実に効かせたい固定文は明示的に登録するのが堅実です。
from google import genai
from google.genai import types
client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
cache = client.caches.create(
model="gemini-2.5-flash",
config=types.CreateCachedContentConfig(
system_instruction="あなたはアプリレビューを5段階の感情で分類する担当者です。",
contents=["(ここに長い分類ルールや判定例をまとめて入れる)"],
ttl="3600s",
),
)
response = client.models.generate_content(
model="gemini-2.5-flash",
contents="星1:起動するたびに落ちます。最悪です。",
config=types.GenerateContentConfig(cached_content=cache.name),
)
usage = response.usage_metadata
print(f"通常入力: {usage.prompt_token_count}")
print(f"キャッシュ読込: {usage.cached_content_token_count}")私のレビュー分類では、数千文字の分類ルールをキャッシュへ移しただけで、リクエストごとの実入力トークンが目に見えて下がりました。
2. 渡すコンテキストを削る
会話履歴をそのまま積み続けると、何往復もするうちに入力トークンが雪だるま式に増えます。古い発言から落として上限内に収める、という単純な整理が効きます。
def prune_history(messages, max_tokens=4000):
"""新しい発言を優先して残し、トークン上限内に収める。"""
kept, total = [], 0
for msg in reversed(messages):
approx = len(msg["content"]) // 2 # 日本語の粗い概算
if total + approx > max_tokens:
break
kept.insert(0, msg)
total += approx
return kept3. 出力トークンに上限をかける
入力に気を取られがちですが、単価が高いのは出力側です。max_output_tokens を用途に合わせて絞るだけで、暴走した長文生成による想定外の請求を防げます。
ストリーミングで無駄な再試行を減らす
長い応答を待っているとタイムアウトしやすく、再送するたびに入力トークンを払い直すことになります。ストリーミングなら生成途中から結果が届くため、体感速度が上がるだけでなく、重複リクエストによる無駄な課金も減らせます。最終チャンクには usage_metadata が載るので、実際の消費量もそのまま記録できます。
from google import genai
client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
stream = client.models.generate_content_stream(
model="gemini-2.5-flash",
contents="このレビュー群を要約して、改善要望を3点にまとめてください。",
)
usage = None
for chunk in stream:
if chunk.text:
print(chunk.text, end="", flush=True)
if chunk.usage_metadata:
usage = chunk.usage_metadata
if usage:
print(f"\n入力 {usage.prompt_token_count} / 出力 {usage.candidates_token_count}")なお count_tokens の呼び出し自体は無料なので、本番のリクエスト前に必ず一度通して、入力側の見積もりをログに残しておくと、後からコストの異常値を追いやすくなります。
次の一歩
まずは手元でいちばん回数の多い処理を一つ選び、count_tokens でその実入力トークンを測ってみてください。多くの場合、毎回貼り付けている固定文やオーバースペックな画像解像度が、削れる余地としてすぐ見つかります。さらにリアルタイム性を高めたいときは、ストリーミングとチャットの実装パターンや、画像・動画を扱うマルチモーダル API ガイドも合わせて読むと、コストと体験の両面で打ち手が増えます。