Using Pydantic and OpenAI for Reliable Structured Outputs
- Authors

- Name
- Nino
- Occupation
- Senior Tech Editor
For years, developers building applications on top of Large Language Models (LLMs) have faced a consistent, nagging headache: reliability. You ask an LLM for a JSON object, and it gives you a string that looks like JSON, but perhaps includes a trailing comma, a conversational preamble, or a key that doesn't exist in your schema. We tried regex, we tried json.loads() inside try-except blocks, and we tried complex prompt engineering.
With the introduction of native Structured Outputs by OpenAI, and the seamless integration with Pydantic, that era of fragility is over. By using n1n.ai, developers can access these robust features across various model versions with optimized latency and high-speed throughput. In this guide, we will dive deep into how to stop parsing JSON by hand and start trusting your model's output using Pydantic.
The Problem: The 'Stochastic' Nature of Text
Traditional LLM interactions are text-in, text-out. When you need to integrate an LLM into a software pipeline—say, to extract data from a medical report or generate a configuration file—you need structured data.
Before Structured Outputs, you might have used a prompt like: "Return a JSON object with keys 'name' and 'age'. Do not include any other text."
Even with this instruction, models like GPT-4o would occasionally fail, especially under high load or complex schemas. This forced developers to write 'defensive code' to clean the output. However, by using a specialized API aggregator like n1n.ai, you can ensure that you are hitting the most stable endpoints that support the latest response_format protocols.
Why Pydantic is the Solution
Pydantic is the most widely used data validation library for Python. It allows you to define the shape of your data using standard Python type hints. When paired with OpenAI's API, Pydantic acts as a bridge between the probabilistic world of LLMs and the deterministic world of software engineering.
Key benefits include:
- Type Safety: Ensure that a field marked as
intis actually an integer. - Validation: Use Pydantic's validators to check if a value falls within a specific range (e.g., age > 0).
- Auto-Documentation: The Pydantic model itself generates the JSON Schema that the LLM uses to constrain its output.
Implementation Guide: Step-by-Step
To get started, ensure you have the latest openai and pydantic libraries installed. We recommend using n1n.ai to manage your API keys and monitor usage across different models like GPT-4o or Claude 3.5 Sonnet.
1. Define Your Schema
First, we define a Pydantic model. Let's imagine we are building a tool to extract information from movie reviews.
from pydantic import BaseModel, Field
from typing import List, Optional
from enum import Enum
class Sentiment(str, Enum):
positive = "positive"
negative = "negative"
neutral = "neutral"
class MovieExtraction(BaseModel):
title: str = Field(description="The name of the movie")
rating: int = Field(description="Rating out of 10", ge=0, le=10)
sentiment: Sentiment
actors: List[str] = Field(default_factory=list)
summary: Optional[str] = None
2. The API Call
OpenAI's SDK now includes a beta.chat.completions.parse method that handles the serialization of the Pydantic model into a JSON schema and the subsequent deserialization of the response back into a Pydantic object.
from openai import OpenAI
client = OpenAI(api_key="YOUR_N1N_API_KEY", base_url="https://api.n1n.ai/v1")
completion = client.beta.chat.completions.parse(
model="gpt-4o-2024-08-06",
messages=[
{"role": "system", "content": "Extract the movie details from the provided text."},
{"role": "user", "content": "I watched Inception last night. It was amazing! 10/10. Leonardo DiCaprio was brilliant."},
],
response_format=MovieExtraction,
)
movie_data = completion.choices[0].message.parsed
print(f"Title: {movie_data.title}")
print(f"Rating: {movie_data.rating}")
Advanced Pattern: Handling Nested Objects and Enums
One of the most powerful features of this approach is the ability to handle nested data structures. If you are building a RAG (Retrieval-Augmented Generation) system, you might want the model to return a list of citations alongside the answer.
class Citation(BaseModel):
source_id: str
quote: str
class AnswerWithCitations(BaseModel):
answer: str
citations: List[Citation]
By passing AnswerWithCitations to the response_format, the LLM is mathematically constrained by the underlying engine to only produce tokens that satisfy this specific JSON structure. This is not just a hint; it is a hard constraint applied during the sampling process.
Comparison: Manual vs. Structured
| Feature | Manual Parsing | Function Calling | Structured Outputs (Pydantic) |
|---|---|---|---|
| Reliability | Low (Regex/JSON.loads) | Medium | High (Schema Constrained) |
| Complexity | High (Custom logic) | Medium | Low (Native Pydantic) |
| Type Safety | None | Limited | Full Python Type Hints |
| Performance | Variable | Good | Excellent |
Pro Tips for Production
- Use Descriptions: The
Field(description="...")parameter is not just for documentation. It is sent to the LLM as part of the schema, acting as a micro-prompt for that specific field. Use it to guide the model on nuances. - Handle Refusals: Sometimes the model may refuse to answer (e.g., for safety reasons). Always check
completion.choices[0].message.refusalbefore accessing the parsed data. - Strict Mode: OpenAI's Structured Outputs require
strict=Truein the background. This means all fields must be present in the output, andadditionalPropertiesare disallowed. This is why Pydantic is so useful—it enforces this strictness at the code level. - Latency Considerations: While structured output ensures accuracy, it can slightly increase time-to-first-token because the model needs to process the schema. Using a high-performance provider like n1n.ai helps mitigate this by routing requests through the fastest available nodes.
Conclusion
The combination of Pydantic and OpenAI represents a massive leap forward in LLM developer experience. It moves the burden of validation from the application logic to the model's inference engine itself. This leads to cleaner code, fewer runtime errors, and a more predictable user experience.
Whether you are building complex agents or simple data extraction pipelines, the structured output pattern is the new gold standard. To start experimenting with these features using the most reliable LLM infrastructure, check out n1n.ai.
Get a free API key at n1n.ai