The official google-genai package lets Python applications call Gemini without assembling HTTP requests by hand. This guide works for FastAPI applications, background jobs, and regular scripts.
Think of an SDK as a translator: you call a readable Python method, and the library
builds the API request and turns the response into a Python object. Network failures,
timeouts, credentials, and result validation still remain your application’s job.
Keep demo mode for the first run. The project then works immediately, while real
Gemini stays an optional feature for students who have a learning key.
Install the official package
Add the dependency to a uv project:
uv add "google-genai>=2,<3"
If pyproject.toml and uv.lock already pin it, do not add it again:
uv sync --frozen --dev
Use this import:
from google import genai
Do not install the legacy google-generativeai package in a new project.
Keep the key out of source code
Pass the key through the environment or application settings:
from google import genai
client = genai.Client(api_key=settings.gemini_api_key)
Never commit a real .env file. Keep only an empty value in .env.example:
GEMINI_API_KEY=
GEMINI_MODEL=gemini-2.5-flash
Model availability can depend on the account and change over time, so keep the model name in settings instead of hard-coding it inside a method.
Make a synchronous call
response = client.models.generate_content(
model=settings.gemini_model,
contents="Explain HTTP status 404 in one sentence",
)
text = (response.text or "").strip()
if not text:
raise RuntimeError("Gemini returned an empty response")
response.text can be empty. Validate it before saving it or returning it to a user.
Do not block FastAPI
Inside an async def handler, use the asynchronous client:
import asyncio
response = await asyncio.wait_for(
client.aio.models.generate_content(
model=settings.gemini_model,
contents=prompt,
),
timeout=settings.gemini_timeout_seconds,
)
asyncio.wait_for limits waiting at the application layer. Map asyncio.TimeoutError to a controlled 504 response and expected provider failures to 502. Do not expose raw exception text because it may contain unnecessary technical details.
A useful mental model: 502 means โthe upstream service failed,โ while 504 means
โwe did not receive its answer in time.โ In both cases the FastAPI application stays
healthy and can invite the user to try again.
Make the integration replaceable
Do not construct the client directly in a route. Put the external service behind a small interface:
from typing import Protocol
class TextProvider(Protocol):
async def improve(self, text: str) -> str: ...
The application can then use:
GeminiProviderin a configured environment;- a deterministic
DemoProviderwithout a key; - a fake provider in tests without a network call.
This keeps the project and CI reproducible and avoids spending quota during tests.
Common failures
ModuleNotFoundErrorโ runuv sync --frozen --dev, then execute code throughuv run.- A
401or403response โ check the key and project access without printing the key to the terminal or logs. - A model returns
404โ check which models your account can use and changeGEMINI_MODEL. - FastAPI hangs during the call โ ensure you call
client.aio...withawait, not the synchronous method. - A test reaches the internet โ install the dependency override before the request and clear overrides afterward.
Readiness check
The integration is ready when the app starts without a real key in demo mode, no secret is committed, the network call has a timeout, an empty response is handled, and tests use a fake provider.
๐ฌ Comments (0)
No comments yet
Be the first to share your opinion about this article!