Free lesson · GenAI Agent Engineering

Make first API call

You will make your first LLM API call. Send a simple prompt to Gemini, print the response text, and explore the response object structure.

Course: LLM Foundations for Agent Builders · Chapter 6 · Your First LLM Call

Free to read — no subscription required.

Introduction

Engineers often spend time reading API documentation without a clear mental model of what a working call actually looks like end-to-end—where authentication goes, how the response is structured, and which parameters matter first. The Gemini API simplifies this: a single generate_content call handles authentication, model dispatch, and response packaging in one round-trip. You'll learn how to initialize a client with an API key, send a text prompt to a hosted model, read back generated text from the response object, and tune output behavior using parameters like temperature, max_output_tokens, and system_instruction. After this lesson, you'll be able to make a fully working LLM API call and interpret the response it returns.

Key Terminology

  • genai.Client — The authenticated entry point to the Gemini API, initialized once with an API key and reused for all model calls in a session.
  • generate_content — The core Gemini API method (client.models.generate_content) that sends a prompt to a hosted model and returns a structured response in a single round-trip.
  • response.text — The string accessor on the response object that surfaces the model's generated text; it is the primary output you read after a generate_content call.
  • GenerateContentConfig — An optional configuration object passed as the config argument to generate_content that controls generation behavior through parameters such as temperature, max_output_tokens, system_instruction, and top_p.
  • temperature — A generation parameter (range 0–1) that controls output randomness; values near 0 produce consistent, deterministic answers while values near 1 introduce creative variation.
  • system_instruction — A persistent behavioral directive inside GenerateContentConfig that shapes the model's voice and constraints for the entire response without appearing in the user-facing prompt passed via contents.

Concepts

Loading diagram...

One Call, One Round-Trip

Making an LLM API call is simpler than it looks: authentication, model dispatch, and response packaging all happen inside a single method call. You initialize a genai.Client with your API key once, and from that point the client handles all communication with the Gemini backend. When you call client.models.generate_content, you send one request and receive one structured response — no persistent connection to manage, no streaming session to open, no teardown step.

This round-trip design also means the client is stateless with respect to individual calls. Everything the model needs — the model identifier, the prompt, and any generation config — travels as arguments to generate_content. Nothing carries over between calls unless you pass it in again explicitly. That predictability makes it easy to reason about what the model receives on any given request.

The Response Object

The return value of generate_content is not a plain string — it is a structured object. response.text is a convenience accessor that extracts the first candidate's generated content, which is all you need for a working first call. Understanding that response.text is a view into a richer structure (one that can hold multiple candidates, finish reasons, and token-count metadata) prepares you for more advanced use later, but the simple accessor is the right starting point.

Separating What from How: Generation Config

Once the basic call works, GenerateContentConfig gives you a clean way to separate what the model should generate from how it should generate it. The contents argument carries the user-facing prompt — the "what." The config argument carries the generation strategy — the "how." Keeping these concerns separate makes it easy to reuse the same prompt with different generation parameters, or the same config with different prompts.

temperature is the most important knob here. Near 0, the model favors its highest-probability tokens, producing repeatable, factual output. Near 1, it samples more broadly, introducing creative variation — useful for story generation, but risky for factual queries. max_output_tokens caps how many tokens the model can emit, keeping costs predictable during development. top_p (nucleus sampling) works alongside temperature to restrict sampling to the most probable token pool; 0.95 is a safe starting point that lets temperature do the creative steering.

system_instruction occupies a different conceptual slot: it is not about randomness or length, but about voice and persistent role. It sets a standing behavioral rule — "You are a concise storyteller. Write vivid, punchy prose." — that the model applies across the entire response without appearing in the conversation turn visible to the user. Think of it as a pre-loaded constraint that shapes everything the model says, separate from the per-request prompt (see Code Walkthrough).

Code Walkthrough

Now that you understand the core building blocks—the client, the model identifier, and the response object—here is how they fit together in a minimal, runnable call.

The entry point is client.models.generate_content. You initialize a genai.Client with your API key loaded from the environment, pass a model name and a text prompt via contents, then read the generated text from response.text:

Code snippetpython
1import os 2from dotenv import load_dotenv 3from google import genai 4 5load_dotenv() 6 7client = genai.Client(api_key=os.environ.get("GEMINI_API_KEY")) 8 9response = client.models.generate_content( 10 model="gemini-2.0-flash", 11 contents="What is Python? Explain in one sentence." 12) 13 14print(response.text)

gemini-2.0-flash is fast and cost-efficient—the right default for most tasks. If a task requires deeper reasoning you can swap in gemini-1.5-pro, but start here.

Once the basic call works, GenerateContentConfig lets you tune how the model generates text. temperature controls creativity: values near 0 produce consistent, deterministic answers; values near 1 introduce variation. max_output_tokens caps response length, which keeps costs predictable during development. system_instruction applies a persistent behavioral constraint without modifying the user-facing prompt—the model treats it as a standing rule for the entire response:

Code snippetpython
1import os 2from dotenv import load_dotenv 3from google import genai 4from google.genai.types import GenerateContentConfig 5 6load_dotenv() 7 8client = genai.Client(api_key=os.environ.get("GEMINI_API_KEY")) 9 10response = client.models.generate_content( 11 model="gemini-2.0-flash", 12 contents="Write a creative story opening.", 13 config=GenerateContentConfig( 14 system_instruction="You are a concise storyteller. Write vivid, punchy prose.", 15 max_output_tokens=120, 16 temperature=0.8, 17 top_p=0.95, 18 ) 19) 20 21print(response.text)

Passing config is optional—the API applies sensible defaults when you omit it—but these levers become essential as soon as you move from experimentation toward production. top_p (nucleus sampling) works alongside temperature to filter improbable tokens; leaving it near 0.95 is a safe starting point.

You'll know it works when both scripts print coherent, non-empty text to the console: the first returning a one-sentence Python definition, the second a short story opening that reflects the voice constraint set in system_instruction.

Do's and Don'ts

Having walked through making Your First LLM API Call above, the following Do's and Don'ts distill it into practice.

Do's

  1. ✓Do load your API key from the environment using os.environ.get("GEMINI_API_KEY") and pass it to genai.Client — hardcoding the key in source is a credential-leak risk; pulling it via load_dotenv() + os.environ.get keeps the key out of version control and lets you rotate it without touching code.
  2. ✓Do start with gemini-2.0-flash as your default model identifier — it is optimized for speed and cost efficiency, making iteration fast; only swap to gemini-1.5-pro once you have a concrete reasoning task that flash cannot handle, so you don't pay reasoning-model costs during early development.
  3. ✓Do pass GenerateContentConfig with max_output_tokens set explicitly during development — without a token cap, an open-ended prompt can return arbitrarily long responses, making cost and latency unpredictable; setting it (e.g., 120) alongside temperature and system_instruction lets you control output length, creativity, and behavioral constraints as a single, composable config object.

Don'ts

  1. ✗Don't read generated output from anywhere other than response.text — the generate_content response object wraps the model's output in a structured envelope; attempting to print response directly or index into it as a plain string will produce unhelpful object representations rather than the generated text.
  2. ✗Don't put behavioral constraints in contents when system_instruction exists — mixing a role directive ("You are a concise storyteller") into the user-facing prompt conflates instruction and input, making prompts brittle to edit; system_instruction in GenerateContentConfig applies the constraint as a standing rule the model respects for the entire response without polluting contents.
  3. ✗Don't omit GenerateContentConfig and assume API defaults are sufficient for production — the API does apply defaults when config is absent, which is fine for a first smoke-test, but temperature near 1 can produce inconsistent answers and an uncapped token budget causes runaway costs; leaving these unset past the exploration phase means behavior is defined by undocumented defaults rather than explicit choices you control.

A hands-on lab comes with this lesson — real code, in a cloud IDE. Create a free account to run it. No card.

Free account · no card · straight to the lab

Or get the full path — from

More free lessons in LLM Foundations for Agent Builders

All free lessons in GenAI Agent Engineering →