# How to Implement Structured JSON Extraction with Claude Using Pydantic Models

> Learn structured JSON extraction with Claude and Pydantic models. Define a schema, embed it in prompts, and parse responses for type-safe structured outputs. Automate validation and coercion.

- Repository: [Anthropic/claude-cookbooks](https://github.com/anthropics/claude-cookbooks)
- Tags: how-to-guide
- Published: 2026-04-14

---

**You can enforce type-safe structured outputs from Claude by defining a Pydantic `BaseModel` to describe your schema, embedding that schema in your system prompt, and parsing Claude's response with `model.parse_raw()` to automatically validate and coerce types.**

The `anthropics/claude-cookbooks` repository provides production-ready patterns for integrating Claude with Pydantic to extract structured JSON. This approach combines Claude's JSON mode with Pydantic's validation layer to turn unstructured text into type-safe Python objects, perfect for building reliable data pipelines and agent systems.

## Why Combine Claude with Pydantic?

Claude can generate syntactically valid JSON when prompted correctly, as demonstrated in `tool_use/extracting_structured_json.ipynb`. However, syntax validity does not guarantee that field names match your expectations or that data types are correct. Pydantic adds a crucial validation layer that:

- **Validates structure**: Ensures all required fields are present and correctly named
- **Coerces types**: Automatically converts strings to integers, floats, or custom types
- **Provides error context**: Returns specific validation failures that enable programmatic retry logic

According to the `anthropics/claude-cookbooks` source code, this combination lets you treat Claude as a **typed tool** where downstream code can depend on a guaranteed schema contract.

## The Four-Step Implementation Workflow

### Step 1: Define the Pydantic Schema

Create a Pydantic model that describes the exact shape of data you expect Claude to return. Use precise types including `Literal` for enums to constrain Claude's output.

```python
from pydantic import BaseModel
from typing import Literal

class WeatherReport(BaseModel):
    location: str
    temperature_c: float
    condition: Literal["sunny", "cloudy", "rainy"]

```

### Step 2: Embed the Schema in Your Prompt

Generate the JSON schema using `model.schema_json()` and include it in your system prompt. This guarantees Claude sees the exact field names and types required.

```python
schema_json = WeatherReport.schema_json(indent=2)

prompt = f"""
You must respond ONLY with a JSON object that matches this schema:

{schema_json}

Here is an example you must follow exactly:
{{"location": "San Francisco", "temperature_c": 18.5, "condition": "cloudy"}}

Provide a weather report for Tokyo.
"""

```

### Step 3: Call Claude with Deterministic Settings

Use the `anthropic` Python client with `temperature=0` to minimize variability that could break the schema. Set `max_tokens` appropriately for your expected payload size.

```python
from anthropic import Anthropic

client = Anthropic()
response = client.messages.create(
    model="claude-3-sonnet-20240229",
    max_tokens=300,
    temperature=0,
    messages=[{"role": "user", "content": prompt}],
)

raw_json = response.content[0].text

```

### Step 4: Parse and Validate the Response

Use `BaseModel.parse_raw()` to convert the JSON string into a validated Python object. This raises a `ValidationError` if Claude's output deviates from your schema.

```python
from pydantic import ValidationError

try:
    report = WeatherReport.parse_raw(raw_json)
    print(f"Validated: {report.location} is {report.condition}")
except ValidationError as e:
    print(f"Validation failed: {e}")

```

## Complete Working Example

This end-to-end script from `tool_use/tool_use_with_pydantic.ipynb` demonstrates a book information extraction pipeline:

```python
from anthropic import Anthropic
from pydantic import BaseModel, ValidationError
from typing import Literal
import json

class BookInfo(BaseModel):
    title: str
    author: str
    pages: int
    genre: Literal["fiction", "nonfiction", "science", "fantasy"]

# Build the prompt with embedded schema

schema_json = BookInfo.schema_json(indent=2)
prompt = f"""
You are a helpful assistant.
Return ONLY a JSON object that matches the following schema:

{schema_json}

Example:
{{"title": "The Great Gatsby", "author": "F. Scott Fitzgerald", "pages": 180, "genre": "fiction"}}

Now provide details for "Sapiens: A Brief History of Humankind".
"""

# Call Claude

client = Anthropic()
response = client.messages.create(
    model="claude-3-sonnet-20240229",
    max_tokens=300,
    temperature=0,
    messages=[{"role": "user", "content": prompt}],
)

# Parse and validate

raw_json = response.content[0].text
try:
    book = BookInfo.parse_raw(raw_json)
    print(f"✅ Validated: {book.title} by {book.author}")
except ValidationError as e:
    print(f"❗ Validation failed: {e}")

```

## Handling Validation Failures with Retry Logic

Production systems should gracefully recover from validation errors. This pattern from the cookbooks augments the prompt with error details for subsequent attempts:

```python
def get_validated_response(prompt: str, model_cls: BaseModel, max_tries: int = 3):
    client = Anthropic()
    for attempt in range(max_tries):
        resp = client.messages.create(
            model="claude-3-sonnet-20240229",
            max_tokens=400,
            temperature=0,
            messages=[{"role": "user", "content": prompt}],
        )
        raw = resp.content[0].text
        try:
            return model_cls.parse_raw(raw)
        except ValidationError as err:
            # Inject error details into prompt for next attempt

            prompt = (
                f"{prompt}\n\nYour previous JSON was invalid:\n{err}\n"
                "Please produce a valid JSON object that matches the schema."
            )
    raise RuntimeError("Failed to obtain valid response after maximum attempts")

```

## Best Practices for Reliable JSON Extraction

**Use `temperature=0`** when deterministic output is required. Higher temperatures introduce variability that can break schema compliance.

**Include concrete examples** in your prompt. The notebook `tool_use/extracting_structured_json.ipynb` shows that providing a sample JSON object helps Claude anchor its output to the exact shape you need.

**Limit `max_tokens`** to a reasonable size for your expected payload. This prevents Claude from adding explanatory text after the JSON block.

**Call `model.schema_json(indent=2)`** rather than manually writing schemas. This ensures the prompt contains the exact field names and type constraints defined in your Pydantic model.

**Catch `ValidationError` explicitly** rather than generic exceptions. Pydantic provides detailed error messages that identify exactly which field failed validation and why.

## Summary

- Define your data contract using **Pydantic `BaseModel`** with precise type hints including `Literal` for constrained choices
- **Embed `model.schema_json()`** directly in your system prompt to communicate the exact schema to Claude
- **Use `temperature=0`** and constrained `max_tokens` to ensure deterministic, focused JSON output
- **Parse responses with `model.parse_raw()`** to automatically validate structure and coerce types
- **Implement retry logic** that feeds validation errors back into the prompt for self-correction

## Frequently Asked Questions

### What is the benefit of using Pydantic with Claude for JSON extraction?

Pydantic provides runtime validation and type coercion that Claude's JSON mode alone cannot guarantee. While Claude produces syntactically valid JSON, Pydantic ensures the structure matches your expected schema, converts strings to appropriate Python types (like `int` or `float`), and raises specific `ValidationError` exceptions that enable programmatic error handling.

### How do I handle invalid JSON responses from Claude?

Wrap your parsing logic in a try/except block that catches `pydantic.ValidationError`. When validation fails, append the error details to your original prompt and resend the request to Claude, as demonstrated in `tool_use/tool_use_with_pydantic.ipynb`. This "self-correction" pattern typically resolves schema violations in the second attempt.

### Which Claude model works best for structured JSON extraction?

The `claude-3-sonnet-20240229` model provides excellent results for structured JSON extraction, as documented in the cookbooks. Newer Claude 3.5 models offer improved instruction following for complex nested schemas. Always use `temperature=0` regardless of model choice to ensure consistent formatting.

### Where can I find complete working examples of Claude and Pydantic integration?

The `anthropics/claude-cookbooks` repository contains several reference implementations:
- **`tool_use/tool_use_with_pydantic.ipynb`**: Complete Pydantic integration workflow
- **`tool_use/extracting_structured_json.ipynb`**: JSON mode behavior and prompting strategies
- **`misc/how_to_enable_json_mode.ipynb`**: Technical details on JSON mode configuration
- **`claude_agent_sdk/00_The_one_liner_research_agent.ipynb`**: Advanced usage within agent architectures