๐Ÿ“ Ai

Google GenAI SDK in Python: synchronous and asynchronous calls

P
Author
PyLand Team
๐Ÿ“…
Published
18.09.2026
โฑ๏ธ
Reading time
2 min
๐Ÿ‘๏ธ
Views
3
๐ŸŒฟ
Level
Medium

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:

  • GeminiProvider in a configured environment;
  • a deterministic DemoProvider without 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 โ€” run uv sync --frozen --dev, then execute code through uv run.
  • A 401 or 403 response โ€” 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 change GEMINI_MODEL.
  • FastAPI hangs during the call โ€” ensure you call client.aio... with await, 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.

Official documentation

Your reaction to the article

๐Ÿ’ฌ Comments (0)

๐Ÿ” Sign in to leave a comment
๐Ÿšช Login
๐Ÿ’ญ

No comments yet

Be the first to share your opinion about this article!

๐Ÿ”— Similar

Similar articles

Continue learning with these materials

๐Ÿ“

if __name__ == "__main__": the Python entry point

The if name == "main" guard defines a Python program's entry point: it distinguishes direct...

๐Ÿ“… 14.08.2026 ๐Ÿ‘๏ธ 125
๐Ÿ“

strip() and lower(): Preparing User Text ๐Ÿงน

A user may enter the correct word with extra spaces or different letter case. Python...

๐Ÿ“… 11.08.2026 ๐Ÿ‘๏ธ 100
๐Ÿ“

ord(), chr(), and Cyclic Letter Shifts in Python ๐Ÿ”

A string contains characters, but a computer stores every character as a numeric code. Python...

๐Ÿ“… 09.08.2026 ๐Ÿ‘๏ธ 177

Did you like the article?

Subscribe to our updates and be the first to receive new articles. Grow with PyLand!