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 agenerate_contentcall.GenerateContentConfig— An optional configuration object passed as theconfigargument togenerate_contentthat controls generation behavior through parameters such astemperature,max_output_tokens,system_instruction, andtop_p.temperature— A generation parameter (range0–1) that controls output randomness; values near0produce consistent, deterministic answers while values near1introduce creative variation.system_instruction— A persistent behavioral directive insideGenerateContentConfigthat shapes the model's voice and constraints for the entire response without appearing in the user-facing prompt passed viacontents.
Concepts
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
- ✓Do load your API key from the environment using
os.environ.get("GEMINI_API_KEY")and pass it togenai.Client— hardcoding the key in source is a credential-leak risk; pulling it viaload_dotenv()+os.environ.getkeeps the key out of version control and lets you rotate it without touching code. - ✓Do start with
gemini-2.0-flashas your default model identifier — it is optimized for speed and cost efficiency, making iteration fast; only swap togemini-1.5-proonce you have a concrete reasoning task that flash cannot handle, so you don't pay reasoning-model costs during early development. - ✓Do pass
GenerateContentConfigwithmax_output_tokensset 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) alongsidetemperatureandsystem_instructionlets you control output length, creativity, and behavioral constraints as a single, composable config object.
Don'ts
- ✗Don't read generated output from anywhere other than
response.text— thegenerate_contentresponse object wraps the model's output in a structured envelope; attempting to printresponsedirectly or index into it as a plain string will produce unhelpful object representations rather than the generated text. - ✗Don't put behavioral constraints in
contentswhensystem_instructionexists — mixing a role directive ("You are a concise storyteller") into the user-facing prompt conflates instruction and input, making prompts brittle to edit;system_instructioninGenerateContentConfigapplies the constraint as a standing rule the model respects for the entire response without pollutingcontents. - ✗Don't omit
GenerateContentConfigand assume API defaults are sufficient for production — the API does apply defaults whenconfigis absent, which is fine for a first smoke-test, buttemperaturenear1can 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
- Ch 1Build data pipelines
- Ch 3Create Pydantic models
- Ch 3Configure Pydantic
- Ch 6Install Gemini SDK
- Ch 6Make first API callYou are here
- Ch 17Compare embedding models