# 07 - Structured Output
**Notebook:** `07-structured-output.ipynb`
## Video
<iframe width="720" height="406" src="https://www.youtube.com/embed/_6gcpKUGKPQ?si=Mcclbj_AZlayw4cy" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
---
## Why Structured Output?
To understand why this is valuable, let's go back to something we might have noticed in notebook 06.
When we asked OpenAI about the bird in a poem:
- One time it came back with "The Raven"
- One time it came back with "The poem features a nightingale"
Both are *right*, but if I wanted to use this for my encyclopedia—maybe I'm trying to get OpenAI to decide on the title of my entry—and one time it gives me "The Raven" and one time "the poem features a nightingale"... I don't actually know how to **extract** the name of the bird from those sentences.
If the input coming back to me is always of a different structure, I don't know how to parse that text to find all the values I need for my encyclopedia.
**That's why we use structured output.**
---
## What is Structured Output?
**Structured output** allows us to **demand** something like a dictionary coming back to us, and to **define the structure** of that dictionary.
Remember when we made our dictionary of poems? We had title, author, year published... we defined the structure of the data for that kind of object. Think about it as building a spreadsheet—every poem will have values for all of those cells.
If we want to accomplish that with AI (which tends to be all over the place if you don't pin it down), we use **structured outputs** from OpenAI.
---
## Setting Up Pydantic
We need to import a tool called **Pydantic** that helps us define the schema:
```python
from openai import OpenAI
from pydantic import BaseModel
client = OpenAI()
```
---
## Defining Your Schema
Here's where we define the schema—the structure we want our response to come back in. We do this using a [**class**](/glossary/coding-basics-py/class):
```python
class BirdPoem(BaseModel):
poem_title: str
poet: str
bird: str
symbolism: str
```
### What's a Class?
A class is like a **blueprint** or **template**. It doesn't hold data itself—it describes what shape the data should have.
Think of it like designing a form:
- "There should be a field called `poem_title` and it should be text"
- "There should be a field called `poet` and it should be text"
- etc.
The `: str` after each field name means "this should be a string (text)."
When you press play, the schema gets defined—Python now knows what a `BirdPoem` looks like.
---
## Making a Structured Request
```python
response = client.responses.parse(
model="gpt-5-mini",
input=[{"role": "user", "content": "Analyze the bird in Poe's The Raven"}],
text_format=BirdPoem
)
result = response.output_parsed
print(result)
```
The key difference: we're saying `text_format=BirdPoem` to tell OpenAI that the response **must** come back in that structure.
Output:
```text
poem_title='The Raven'
poet='Edgar Allan Poe'
bird='Raven'
symbolism='Poe's raven functions as a multi-layered symbol...'
```
Now we're getting **predictable, structured data** back every single time!
---
## Accessing the Fields
Once you have structured output, you can access specific fields using [**dot notation**](/glossary/coding-basics-py/dot-notation):
```python
print(result.poem_title) # The Raven
print(result.poet) # Edgar Allan Poe
print(result.bird) # Raven
print(result.symbolism) # Poe's raven functions as...
```
Notice we use `result.poem_title` (with a dot) instead of `result["poem_title"]` (with brackets). That's because `result` is a class instance, not a dictionary.
This is incredibly powerful for building things like our encyclopedia—we can reliably extract exactly the fields we need.
---
## More Complex Schemas
You can add more fields and even use lists:
```python
class EncyclopediaEntry(BaseModel):
title: str
bird_species: str
literary_significance: str
themes: list[str]
era: str
art_prompt: str
```
Now OpenAI will return data with all these fields filled in, and `themes` will be a list of strings!
---
## Try It: Remix the Schema
You won't feel like you've owned this until you've remixed it and it's doing something you intended to do.
Try:
- Change the `BaseModel` class
- Add a couple more properties
- Remove some properties
- Ask Gemini if you're unsure of the syntax
**Always, always, always ask Gemini for help. Do your best to understand what it's doing.**
---
## Summary
In this notebook, you learned:
- **Structured output** gives you predictable, parseable data from AI
- Use **Pydantic** and `BaseModel` to define your schema
- The schema specifies what **fields** and **types** you expect
- Access fields with **dot notation** (`result.field_name`)
- This is essential for building things like encyclopedias and card decks
Next up: **files** — reading input and saving output!