← chapter

First API call

Chapter 2 · OpenAI and Gemini, one hour, two SDKs

The hour

The plumbing first

OpenAI in one line

response = client.responses.create(
    model="gpt-5.4-nano",
    input="...",
)
print(response.output_text)

OpenAI() reads OPENAI_API_KEY itself.

Gemini in one line

client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
response = client.models.generate_content(
    model="gemini-3.1-flash-lite",
    contents="...",
)
print(response.text)

The three differences

Same job. Same shape.

Put it to work — three web apps

Each one is its own docker compose up project. Browser tab in, AI answer out.

One backend, two SDKs

PROVIDER = os.environ.get("PROVIDER", "openai")
ai = importlib.import_module(f"ai_{PROVIDER}")
# POST /api/ai -> ai.run(req.input)

PROVIDER=gemini docker compose up --build swaps the SDK without touching main.py. The same loader is in all three showcases, byte for byte.

Run any of them

cd code/showcase/<slug>
bash bootstrap-secrets.sh
docker compose up --build
# open http://localhost:3000

Three showcases. Same shape. Still one API call each.

Takeaway

Get the plumbing reflexive now. Key in the environment, never in the source. Load it before the client. Fail loud and early. Every later chapter stands on this.