Getting Structured JSON Output from an LLM
Introduction
Applications need data they can rely on, not paragraphs they must parse. Asking nicely for JSON often works, but occasionally returns extra text or missing fields. Structured outputs constrain the response to a schema you define, so your code receives valid, typed data. This lesson shows two ways to do it in Python.
Prerequisites
- Calling an LLM API lesson
- pip install anthropic pydantic
Step-by-Step Guide
Step 1: Define the shape of the data
Describe what you want back with a Pydantic model. Each field has a name and a type, and the model doubles as validation and documentation.
from pydantic import BaseModel
from typing import List
class ContactInfo(BaseModel):
name: str
email: str
plan: str
interests: List[str]
demo_requested: boolStep 2: Parse the response into your model
The SDK's parse helper takes your model and returns a validated instance, so you work with real Python objects instead of text.
import anthropic
client = anthropic.Anthropic()
response = client.messages.parse(
model="claude-opus-5-5",
max_tokens=16000,
messages=[{
"role": "user",
"content": "Extract: Jane Doe (jane@co.com) wants Enterprise, interested in API and SDKs, wants a demo.",
}],
output_format=ContactInfo,
)
contact = response.parsed_output
print(contact.name, contact.interests)Step 3: Or supply a raw JSON schema
If you are not using Pydantic, pass a JSON schema in the output configuration. The response is then guaranteed to be valid JSON matching it, which you load with the json module. Set additionalProperties to false and list required fields.
import json
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=16000,
messages=[{"role": "user", "content": "Extract info: John Smith (john@example.com) wants the Enterprise plan."}],
output_config={
"format": {
"type": "json_schema",
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"email": {"type": "string"},
"plan": {"type": "string"},
},
"required": ["name", "email", "plan"],
"additionalProperties": False,
},
}
},
)
text = next(b.text for b in response.content if b.type == "text")
data = json.loads(text)Step 4: Still validate the meaning
A schema guarantees the shape, not the truth. A valid email field can still hold the wrong email. Add business checks in your code, such as format rules and cross-checks with other data, and decide what to do with records that fail.
Step 5: Handle failures and keep the schema simple
Check the stop reason before trusting a response, since a request can end early or be declined. Keep schemas small and fields well named, use enums for fixed choices, and make fields optional only when the data may legitimately be absent.
FAQs
- Why not just ask for JSON in the prompt?
- It usually works but can fail unpredictably with extra commentary, code fences, or missing keys. Schema-constrained output removes that class of failure and the parsing code that went with it.
- Does structured output work with every model and feature?
- Support varies by model and provider, and some features, such as certain citation options, cannot be combined with it. Check the documentation for your model.
Summary
Define a schema, let the SDK constrain and parse the output, and keep validating the content in your own code. You get typed data you can trust to have the right shape.
Comments
Log in or create a free account to join the discussion.