# Get Reliable JSON Out of the Claude API

> Constrain replies with output_config.format, or use messages.parse with Pydantic and Zod, so the Claude API returns JSON that always validates.

- Canonical: https://guides-ai.pages.dev/guides/claude-api-structured-output/
- Plate 15.03 · Topic: Building with the API (https://guides-ai.pages.dev/topics/api/)
- Published: 06 Sept 2026 · 3 min read
- Source site: guides-ai — https://guides-ai.pages.dev/

"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

```python
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

```python
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

```typescript
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

```json
{
  "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](/guides/claude-api-tool-use/) · [OpenAI structured outputs](/guides/openai-api-structured-outputs/).
