Structured output
Asking a model for JSON in words works until it doesn't. See how a schema in response_format constrains what Gemini can generate, and why you still validate the values yourself.
Most apps don’t show the model’s answer to a person. They feed it to code: save it to a database, call an API, fill a form. Code needs the same fields, with the same names and types, every single time.
You can ask for JSON in the prompt, and often you’ll get it. But “often” isn’t good enough for a pipeline that runs ten thousand times a day. Structured output moves the format from a request into a rule. You pass a JSON Schema (a description of the fields and their types) in response_format, and the API only lets the model generate text that fits it.
The running example turns a customer’s refund email into a RefundRequest record. Press Play to see the prompt-only approach break, then switch the Try it selector to the schema version, and finally to the case where valid JSON still breaks one of your rules.
- Request
- Generate
- Validate
- Use it
Press Play to watch it run, or step through with ← →.
import json
from google import genai
client = genai.Client()
email = """Hi, I paid Rs 1,299 by UPI for order ORD-4417 on Monday.
The parcel never came and the app still says 'shipped'. Please refund."""
# Asking for JSON in words: a request, not a guarantee.
interaction = client.interactions.create(
model="gemini-3.8-flash",
input=f"Return order_id, amount_inr and reason from this email as JSON:\n{email}",
)
data = json.loads(interaction.output_text) # fails if the reply isn't pure JSON
print(data["amount_inr"]) # fails if the model picked another field nameParsed result
Nothing parsed yet. Press Play.
What to notice
- A schema is enforced while the text is generated, not checked afterwards. The API compiles your schema into a state machine that limits which tokens the model can pick next. That’s why the reply can’t start with “Sure!” or use a field name you didn’t define.
- Pydantic writes the schema for you. In Python you describe the result as a class and pass
RefundRequest.model_json_schema(). The same class parses the reply withmodel_validate_json, so you get a typed object back. - Descriptions are instructions. The type says what kind of value a field holds (
amount_inrmust be an integer). Thedescriptionsays which value: “Amount in rupees, digits only” tells the model to put1299there. Write descriptions as clearly as you would write a prompt. - Use
enum(orLiteral) for categories.reasoncan only be one of four values. That turns a free-text guess into a classification your code can switch on. - Valid JSON isn’t the same as correct data. Some JSON Schema keywords aren’t enforced during generation:
minLength,maxLength,minProperties,maxProperties,uniqueItems,contains,minContainsandmaxContains. The API strips them silently.minItemsandmaxItemsare enforced. Check everything else in your own code.
Structured output
Formats the model’s final answer. Use it when you want the answer as data: extraction, classification, inputs for another system.
Function calling
Lets the model ask your code to do something before it answers: look up an order, book a slot. See Function calling.
400 INVALID_ARGUMENT error. Google’s fix list: flatten nested objects, shorten property names, trim long enums and optional fields, and move soft rules into descriptions.