Skip to main content
Chapter 3 Prompt Engineering Mastery

Structured Output and JSON Mode

16 min read Lesson 11 / 28

Structured Output: Getting Reliable JSON from Claude

AI agents frequently need to parse Claude's output programmatically. Reliable structured output is not optional — it is the foundation of robust agentic pipelines.

Technique 1: Instruct + Parse

The simplest approach: instruct Claude to output JSON, then parse it.

import json
import anthropic

client = anthropic.Anthropic()

def extract_entities(text: str) -> dict:
    response = client.messages.create(
        model="claude-haiku-4-5",
        max_tokens=1024,
        system="""Extract entities from text.
Respond ONLY with valid JSON. No markdown, no explanation.
Schema: {"people": [], "organizations": [], "locations": [], "dates": []}""",
        messages=[{"role": "user", "content": text}],
    )

    raw = response.content[0].text.strip()

    # Remove markdown code fences if present
    if raw.startswith("```"):
        raw = raw.split("```")[1]
        if raw.startswith("json"):
            raw = raw[4:]
        raw = raw.strip()

    return json.loads(raw)


result = extract_entities(
    "Apple CEO Tim Cook announced a new product in Cupertino on March 15th."
)
print(result)
# {"people": ["Tim Cook"], "organizations": ["Apple"], "locations": ["Cupertino"], "dates": ["March 15th"]}

Technique 2: Pydantic + Instructor

The instructor library wraps the Anthropic SDK and automatically validates output against a Pydantic schema:

import instructor
import anthropic
from pydantic import BaseModel, Field

client = instructor.from_anthropic(anthropic.Anthropic())

class ProductReview(BaseModel):
    sentiment: str = Field(description="positive, negative, or neutral")
    score: int = Field(ge=1, le=5, description="Rating from 1 to 5")
    key_points: list[str] = Field(description="Main points from the review")
    would_recommend: bool

review_text = "Great battery life and build quality. Camera could be better. Overall happy with purchase."

result = client.chat.completions.create(
    model="claude-haiku-4-5",
    max_tokens=512,
    response_model=ProductReview,
    messages=[{"role": "user", "content": f"Analyze this review: {review_text}"}],
)

print(result.sentiment)       # positive
print(result.score)           # 4
print(result.would_recommend) # True

JavaScript with Zod Validation

import Anthropic from "@anthropic-ai/sdk";
import { z } from "zod";

const client = new Anthropic();

const ContactSchema = z.object({
  name: z.string(),
  email: z.string().email(),
  phone: z.string().optional(),
  company: z.string().optional(),
});

async function extractContact(text: string) {
  const response = await client.messages.create({
    model: "claude-haiku-4-5",
    max_tokens: 512,
    system:
      "Extract contact information as JSON. Output only valid JSON, no markdown.",
    messages: [{ role: "user", content: text }],
  });

  const raw = response.content[0].text.trim();
  const parsed = JSON.parse(raw);
  return ContactSchema.parse(parsed); // Throws if schema invalid
}

const contact = await extractContact(
  "Reach out to Sarah Chen at sarah@acme.com or call 555-0100."
);

Always validate parsed JSON against a schema. Never assume Claude's output matches your expected format without checking.