Building with the API

Get Reliable JSON Out of the Claude API

3 min read

“Reply with JSON only” is a hope, not a guarantee. Structured outputs are a request parameter — the API constrains generation to your schema.

Raw JSON schema

import json
import anthropic

client = anthropic.Anthropic()  # reads ANTHROPIC_API_KEY

response = client.messages.create(
    model="claude-opus-5",
    max_tokens=1024,
    messages=[{
        "role": "user",
        "content": "Extract: 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,
            },
        }
    },
)

data = json.loads(next(b.text for b in response.content if b.type == "text"))
print(data["email"])  # john@example.com

Forces the whole reply to be one JSON object matching the schema, so json.loads cannot fail on a stray “Here you go:”. List every field in required and set additionalProperties: false — that’s the shape the docs use and it keeps the output tight.

The parameter is output_config.format. An older top-level output_format field is deprecated; don’t reach for it in new code.

Python with Pydantic

from pydantic import BaseModel
import anthropic

class Contact(BaseModel):
    name: str
    email: str
    plan: str

client = anthropic.Anthropic()

response = client.messages.parse(
    model="claude-opus-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Extract: Jane Doe (jane@co.com), Enterprise."}],
    output_format=Contact,
)

print(response.parsed_output.email)  # jane@co.com

messages.parse() derives the schema from the model class and hands back a validated Contact instance on parsed_output — no manual json.loads.

TypeScript with Zod

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

const Contact = z.object({
  name: z.string(),
  email: z.string(),
  plan: z.string(),
});

const client = new Anthropic(); // reads ANTHROPIC_API_KEY

const response = await client.messages.parse({
  model: "claude-opus-5",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Extract: Jane Doe (jane@co.com), Enterprise." }],
  output_config: { format: zodOutputFormat(Contact) },
});

console.log(response.parsed_output.email);

Same guarantee, and parsed_output is typed from the Zod schema.

Schema-valid tool arguments

{
  "name": "book_flight",
  "description": "Book a flight to a destination.",
  "strict": true,
  "input_schema": {
    "type": "object",
    "properties": {
      "destination": { "type": "string" },
      "passengers": { "type": "integer" }
    },
    "required": ["destination", "passengers"],
    "additionalProperties": false
  }
}

strict: true is the tool-side twin: it constrains the arguments Claude sends you, not the reply text. Structured outputs and strict tools are independent and can be used in the same request.


Next: tool use · OpenAI structured outputs.

Open the full interactive version (with copy buttons) ↗

← All guides