Getting Structured Output from LLMs
The hardest part of building LLM applications is getting reliable, parseable output. Here is what works.
The Problem
You ask an LLM for JSON and get:
Sure\! Here is the JSON you requested:
```json
{"name": "John", "age": 30}
Hope that helps!
Good luck parsing that.
## Solution 1: Tool Use / Function Calling
The most reliable approach. Define a schema, let the model fill it:
```python
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=1024,
tools=[{
"name": "extract_person",
"description": "Extract person information from text",
"input_schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"occupation": {"type": "string"}
},
"required": ["name"]
}
}],
tool_choice={"type": "tool", "name": "extract_person"},
messages=[{
"role": "user",
"content": "John is a 30-year-old software engineer from Boston."
}]
)
# Guaranteed valid JSON matching the schema
data = response.content[0].input
# {"name": "John", "age": 30, "occupation": "software engineer"}
This works because the model is constrained to produce valid tool input.
Solution 2: Pydantic + Instructor
The instructor library wraps tool use with Pydantic validation:
import instructor
from pydantic import BaseModel
client = instructor.from_anthropic(anthropic.Anthropic())
class Person(BaseModel):
name: str
age: int | None = None
occupation: str | None = None
person = client.messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=1024,
messages=[{
"role": "user",
"content": "John is a 30-year-old software engineer."
}],
response_model=Person
)
print(person.name) # "John"
print(person.age) # 30
Clean, type-safe, and handles retries on validation failure.
Solution 3: Prefilled Response
For Claude specifically, you can prefill the assistant response:
response = client.messages.create(
model="claude-sonnet-4-5-20250929",
max_tokens=1024,
messages=[
{"role": "user", "content": "Extract: John, 30, engineer. Return JSON."},
{"role": "assistant", "content": "{"}
]
)
# Model continues from "{" and produces valid JSON
result = "{" + response.content[0].text
Hacky but effective for simple cases.
Which to Use
| Approach | Reliability | Complexity | Best For |
|---|---|---|---|
| Tool use | Highest | Medium | Production apps |
| Instructor/Pydantic | Highest | Low | Python apps |
| Prefilled response | Medium | Low | Quick scripts |
| Prompt engineering | Low | Low | Prototyping |
Start with tool use or Instructor. Fall back to prompt engineering only for exploratory work.