← Back to daily report

build-with-claude/structured-outputs.md

Changed on 2026-04-07 14:57:11 EST

+1 lines added
-1 lines removed
Visual Diff
# Structured outputs¶

Get validated JSON results from agent workflows¶

---¶

Structured outputs constrain Claude's responses to follow a specific schema, ensuring valid, parseable output for downstream processing. Two complementary features are available:¶

- **JSON outputs** (`output_config.format`): Get Claude's response in a specific JSON format¶
- **Strict tool use** (`strict: true`): Guarantee schema validation on tool names and inputs¶

These features can be used independently or together in the same request.¶

<Note>¶
Structured outputs are generally available on the Claude API and Amazon Bedrock for
[Claude Mythos Preview](https://anthropic.com/glasswing), Claude Opus 4.6, Claude Sonnet 4.6, Claude Sonnet 4.5, Claude Opus 4.5, and Claude Haiku 4.5. Structured outputs are in beta on Microsoft Foundry. Structured outputs are not supported on Google Cloud's Vertex AI for Claude Mythos Preview.
</Note>¶

<Note>¶
This feature qualifies for [Zero Data Retention (ZDR)](/docs/en/build-with-claude/api-and-data-retention) with limited technical retention. See the [Data retention](#data-retention) section for details on what is retained and why.¶
</Note>¶

<Tip>¶
**Migrating from beta?** The `output_format` parameter has moved to `output_config.format`, and beta headers are no longer required. The old beta header (`structured-outputs-2025-11-13`) and `output_format` parameter will continue working for a transition period. See code examples below for the updated API shape.¶
</Tip>¶

## Why use structured outputs¶

Without structured outputs, Claude can generate malformed JSON responses or invalid tool inputs that break your applications. Even with careful prompting, you may encounter:¶
- Parsing errors from invalid JSON syntax¶
- Missing required fields¶
- Inconsistent data types¶
- Schema violations requiring error handling and retries¶

Structured outputs guarantee schema-compliant responses through constrained decoding:¶
- **Always valid**: No more `JSON.parse()` errors¶
- **Type safe**: Guaranteed field types and required fields¶
- **Reliable**: No retries needed for schema violations¶

## JSON outputs¶

JSON outputs control Claude's response format, ensuring Claude returns valid JSON matching your schema. Use JSON outputs when you need to:¶

- Control Claude's response format¶
- Extract data from images or text¶
- Generate structured reports¶
- Format API responses¶

### Quick start¶

<CodeGroup>¶

```bash Shell¶
curl https://api.anthropic.com/v1/messages \¶
-H "content-type: application/json" \¶
-H "x-api-key: $ANTHROPIC_API_KEY" \¶
-H "anthropic-version: 2023-06-01" \¶
-d '{¶
"model": "claude-opus-4-6",¶
"max_tokens": 1024,¶
"messages": [¶
{¶
"role": "user",¶
"content": "Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan and wants to schedule a demo for next Tuesday at 2pm."¶
}¶
],¶
"output_config": {¶
"format": {¶
"type": "json_schema",¶
"schema": {¶
"type": "object",¶
"properties": {¶
"name": {"type": "string"},¶
"email": {"type": "string"},¶
"plan_interest": {"type": "string"},¶
"demo_requested": {"type": "boolean"}¶
},¶
"required": ["name", "email", "plan_interest", "demo_requested"],¶
"additionalProperties": false¶
}¶
}¶
}¶
}'¶
```¶

```python Python hidelines={1..2}¶
import anthropic¶

client = anthropic.Anthropic()¶

response = client.messages.create(¶
model="claude-opus-4-6",¶
max_tokens=1024,¶
messages=[¶
{¶
"role": "user",¶
"content": "Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan and wants to schedule a demo for next Tuesday at 2pm.",¶
}¶
],¶
output_config={¶
"format": {¶
"type": "json_schema",¶
"schema": {¶
"type": "object",¶
"properties": {¶
"name": {"type": "string"},¶
"email": {"type": "string"},¶
"plan_interest": {"type": "string"},¶
"demo_requested": {"type": "boolean"},¶
},¶
"required": ["name", "email", "plan_interest", "demo_requested"],¶
"additionalProperties": False,¶
},¶
}¶
},¶
)¶
print(response.content[0].text)¶
```¶

```typescript TypeScript hidelines={1..2}¶
import Anthropic from "@anthropic-ai/sdk";¶

const client = new Anthropic({¶
apiKey: process.env.ANTHROPIC_API_KEY¶
});¶

const response = await client.messages.create({¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
messages: [¶
{¶
role: "user",¶
content:¶
"Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan and wants to schedule a demo for next Tuesday at 2pm."¶
}¶
],¶
output_config: {¶
format: {¶
type: "json_schema",¶
schema: {¶
type: "object",¶
properties: {¶
name: { type: "string" },¶
email: { type: "string" },¶
plan_interest: { type: "string" },¶
demo_requested: { type: "boolean" }¶
},¶
required: ["name", "email", "plan_interest", "demo_requested"],¶
additionalProperties: false¶
}¶
}¶
}¶
});¶
const textBlock = response.content.find((block) => block.type === "text");¶
if (textBlock && textBlock.type === "text") {¶
console.log(textBlock.text);¶
}¶
```¶

```csharp C#¶
using System.Collections.Generic;¶
using System.Text.Json;¶
using Anthropic;¶
using Anthropic.Models.Messages;¶

AnthropicClient client = new();¶

var parameters = new MessageCreateParams¶
{¶
Model = Model.ClaudeOpus4_6,¶
MaxTokens = 1024,¶
Messages = [new() { Role = Role.User, Content = "Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan." }],¶
OutputConfig = new OutputConfig¶
{¶
Format = new JsonOutputFormat¶
{¶
Schema = new Dictionary<string, JsonElement>¶
{¶
["type"] = JsonSerializer.SerializeToElement("object"),¶
["properties"] = JsonSerializer.SerializeToElement(new¶
{¶
name = new { type = "string" },¶
email = new { type = "string" },¶
plan_interest = new { type = "string" },¶
demo_requested = new { type = "boolean" },¶
}),¶
["required"] = JsonSerializer.SerializeToElement(new[] { "name", "email", "plan_interest", "demo_requested" }),¶
["additionalProperties"] = JsonSerializer.SerializeToElement(false),¶
},¶
},¶
},¶
};¶

var message = await client.Messages.Create(parameters);¶
Console.WriteLine(message);¶
```¶

```go Go hidelines={1..10,-1}¶
package main¶

import (¶
"context"¶
"fmt"¶

"github.com/anthropics/anthropic-sdk-go"¶
)¶

func main() {¶
client := anthropic.NewClient()¶

response, _ := client.Messages.New(context.Background(),¶
anthropic.MessageNewParams{¶
Model: anthropic.ModelClaudeOpus4_6,¶
MaxTokens: 1024,¶
Messages: []anthropic.MessageParam{¶
anthropic.NewUserMessage(¶
anthropic.NewTextBlock("Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan."),¶
),¶
},¶
OutputConfig: anthropic.OutputConfigParam{¶
Format: anthropic.JSONOutputFormatParam{¶
Schema: map[string]interface{}{¶
"type": "object",¶
"properties": map[string]interface{}{¶
"name": map[string]string{"type": "string"},¶
"email": map[string]string{"type": "string"},¶
"plan_interest": map[string]string{"type": "string"},¶
"demo_requested": map[string]string{"type": "boolean"},¶
},¶
"required": []string{"name", "email", "plan_interest", "demo_requested"},¶
"additionalProperties": false,¶
},¶
},¶
},¶
})¶

fmt.Println(response.Content[0].Text)¶
}¶
```¶

```java Java hidelines={1..7}¶
import com.anthropic.client.AnthropicClient;¶
import com.anthropic.client.okhttp.AnthropicOkHttpClient;¶
import com.anthropic.models.messages.MessageCreateParams;¶
import com.anthropic.models.messages.StructuredMessage;¶
import com.anthropic.models.messages.StructuredMessageCreateParams;¶
import com.anthropic.models.messages.Model;¶

class ContactInfo {¶
public String name;¶
public String email;¶
public String plan_interest;¶
public boolean demo_requested;¶
}¶

public class StructuredOutputQuickStart {¶
public static void main(String[] args) {¶
AnthropicClient client = AnthropicOkHttpClient.fromEnv();¶

StructuredMessageCreateParams<ContactInfo> params = MessageCreateParams.builder()¶
.model(Model.CLAUDE_OPUS_4_6)¶
.maxTokens(1024)¶
.addUserMessage("Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan.")¶
.outputConfig(ContactInfo.class)¶
.build();¶

StructuredMessage<ContactInfo> response = client.messages().create(params);¶
ContactInfo contact = response.content().get(0).asText().text();¶
System.out.println(contact.name + " (" + contact.email + ")");¶
}¶
}¶
```¶

```php PHP hidelines={1..4}¶
<?php¶

use Anthropic\Client;¶

$client = new Client(¶
apiKey: getenv("ANTHROPIC_API_KEY")¶
);¶

$response = $client->messages->create(¶
maxTokens: 1024,¶
messages: [¶
[¶
'role' => 'user',¶
'content' => 'Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan.'¶
]¶
],¶
model: 'claude-opus-4-6',¶
outputConfig: [¶
'format' => [¶
'type' => 'json_schema',¶
'schema' => [¶
'type' => 'object',¶
'properties' => [¶
'name' => ['type' => 'string'],¶
'email' => ['type' => 'string'],¶
'plan_interest' => ['type' => 'string'],¶
'demo_requested' => ['type' => 'boolean']¶
],¶
'required' => ['name', 'email', 'plan_interest', 'demo_requested'],¶
'additionalProperties' => false¶
]¶
]¶
],¶
);¶

echo $response->content[0]->text;¶
```¶

```ruby Ruby hidelines={1..2}¶
require "anthropic"¶

client = Anthropic::Client.new¶

response = client.messages.create(¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
messages: [¶
{¶
role: "user",¶
content: "Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan."¶
}¶
],¶
output_config: {¶
format: {¶
type: "json_schema",¶
schema: {¶
type: "object",¶
properties: {¶
name: { type: "string" },¶
email: { type: "string" },¶
plan_interest: { type: "string" },¶
demo_requested: { type: "boolean" }¶
},¶
required: ["name", "email", "plan_interest", "demo_requested"],¶
additionalProperties: false¶
}¶
}¶
}¶
)¶

puts response.content[0].text¶
```¶

</CodeGroup>¶

**Response format:** Valid JSON matching your schema in `response.content[0].text`¶

```json¶
{¶
"name": "John Smith",¶
"email": "john@example.com",¶
"plan_interest": "Enterprise",¶
"demo_requested": true¶
}¶
```¶

### How it works¶

<Steps>¶
<Step title="Define your JSON schema">¶
Create a JSON schema that describes the structure you want Claude to follow. The schema uses standard JSON Schema format with some limitations (see [JSON Schema limitations](#json-schema-limitations)).¶
</Step>¶
<Step title="Add the output_config.format parameter">¶
Include the `output_config.format` parameter in your API request with `type: "json_schema"` and your schema definition.¶
</Step>¶
<Step title="Parse the response">¶
Claude's response is valid JSON matching your schema, returned in `response.content[0].text`.¶
</Step>¶
</Steps>¶

### Working with JSON outputs in SDKs¶

The SDKs provide helpers that make it easier to work with JSON outputs, including schema transformation, automatic validation, and integration with popular schema libraries.¶

<Note>¶
SDK helper methods (like `.parse()` and Pydantic/Zod integration) still accept `output_format` as a convenience parameter. The SDK handles the translation to `output_config.format` internally. The examples below show the SDK helper syntax.¶
</Note>¶

#### Using native schema definitions¶

Instead of writing raw JSON schemas, you can use familiar schema definition tools in your language:¶

- **Python**: [Pydantic](https://docs.pydantic.dev/) models with `client.messages.parse()`¶
- **TypeScript**: [Zod](https://zod.dev/) schemas with `zodOutputFormat()`¶
- **Java**: Plain Java classes with automatic schema derivation via `outputConfig(Class<T>)`¶
- **Ruby**: `Anthropic::BaseModel` classes with `output_config: {format: Model}`¶
- **C#**, **Go**, **PHP**: Raw JSON schemas passed via `output_config`¶

<CodeGroup>¶

```python Python¶
from pydantic import BaseModel¶
from anthropic import Anthropic¶


class ContactInfo(BaseModel):¶
name: str¶
email: str¶
plan_interest: str¶
demo_requested: bool¶


client = Anthropic()¶

response = client.messages.parse(¶
model="claude-opus-4-6",¶
max_tokens=1024,¶
messages=[¶
{¶
"role": "user",¶
"content": "Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan and wants to schedule a demo for next Tuesday at 2pm.",¶
}¶
],¶
output_format=ContactInfo,¶
)¶

print(response.parsed_output)¶
```¶

```typescript TypeScript hidelines={1}¶
import Anthropic from "@anthropic-ai/sdk";¶
import { z } from "zod";¶
import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";¶

const ContactInfoSchema = z.object({¶
name: z.string(),¶
email: z.string(),¶
plan_interest: z.string(),¶
demo_requested: z.boolean()¶
});¶

const client = new Anthropic();¶

const response = await client.messages.parse({¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
messages: [¶
{¶
role: "user",¶
content:¶
"Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan and wants to schedule a demo for next Tuesday at 2pm."¶
}¶
],¶
output_config: { format: zodOutputFormat(ContactInfoSchema) }¶
});¶

// Automatically parsed and validated¶
console.log(response.parsed_output);¶
```¶

```csharp C#¶
using System.Text.Json;¶
using Anthropic;¶
using Anthropic.Models.Messages;¶

var client = new AnthropicClient();¶

var response = await client.Messages.Create(new MessageCreateParams¶
{¶
Model = "claude-opus-4-6",¶
MaxTokens = 1024,¶
Messages = [new() {¶
Role = Role.User,¶
Content = "Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan and wants to schedule a demo for next Tuesday at 2pm."¶
}],¶
OutputConfig = new OutputConfig¶
{¶
Format = new JsonOutputFormat¶
{¶
Schema = new Dictionary<string, JsonElement>¶
{¶
["type"] = JsonSerializer.SerializeToElement("object"),¶
["properties"] = JsonSerializer.SerializeToElement(new¶
{¶
name = new { type = "string" },¶
email = new { type = "string" },¶
plan_interest = new { type = "string" },¶
demo_requested = new { type = "boolean" },¶
}),¶
["required"] = JsonSerializer.SerializeToElement(¶
new[] { "name", "email", "plan_interest", "demo_requested" }),¶
["additionalProperties"] = JsonSerializer.SerializeToElement(false),¶
},¶
},¶
},¶
});¶

var json = (response.Content.First().Value as TextBlock)!.Text;¶
// JSON is guaranteed to match the schema¶
var contact = JsonSerializer.Deserialize<Dictionary<string, object>>(json);¶
Console.WriteLine($"{contact["name"]} ({contact["email"]})");¶
```¶

```go Go hidelines={1..2,4..7,27..29,-1}¶
package main¶

import (¶
"context"¶
"encoding/json"¶
"fmt"¶

"github.com/anthropics/anthropic-sdk-go"¶
"github.com/invopop/jsonschema"¶
)¶

type ContactInfo struct {¶
Name string `json:"name" jsonschema:"description=Full name"`¶
Email string `json:"email" jsonschema:"description=Email address"`¶
PlanInterest string `json:"plan_interest" jsonschema:"description=Plan type"`¶
DemoRequested bool `json:"demo_requested" jsonschema:"description=Whether a demo was requested"`¶
}¶

func generateSchema(v any) map[string]any {¶
r := jsonschema.Reflector{AllowAdditionalProperties: false, DoNotReference: true}¶
s := r.Reflect(v)¶
b, _ := json.Marshal(s)¶
var m map[string]any¶
json.Unmarshal(b, &m)¶
return m¶
}¶

func main() {¶
client := anthropic.NewClient()¶
schema := generateSchema(&ContactInfo{})¶

message, _ := client.Messages.New(context.TODO(), anthropic.MessageNewParams{¶
Model: anthropic.ModelClaudeOpus4_6,¶
MaxTokens: 1024,¶
Messages: []anthropic.MessageParam{¶
anthropic.NewUserMessage(anthropic.NewTextBlock(¶
"Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan and wants to schedule a demo for next Tuesday at 2pm.",¶
)),¶
},¶
OutputConfig: anthropic.OutputConfigParam{¶
Format: anthropic.JSONOutputFormatParam{¶
Schema: schema,¶
},¶
},¶
})¶

for _, block := range message.Content {¶
switch variant := block.AsAny().(type) {¶
case anthropic.TextBlock:¶
var contact ContactInfo¶
json.Unmarshal([]byte(variant.Text), &contact)¶
fmt.Printf("%s (%s)\n", contact.Name, contact.Email)¶
}¶
}¶
}¶
```¶

```java Java hidelines={1..7,14..16,-2..}¶
import com.anthropic.client.AnthropicClient;¶
import com.anthropic.client.okhttp.AnthropicOkHttpClient;¶
import com.anthropic.models.messages.MessageCreateParams;¶
import com.anthropic.models.messages.StructuredMessage;¶
import com.anthropic.models.messages.StructuredMessageCreateParams;¶
import com.anthropic.models.messages.Model;¶

class ContactInfo {¶
public String name;¶
public String email;¶
public String planInterest;¶
public boolean demoRequested;¶
}¶

public class NativeSchemaExample {¶
public static void main(String[] args) {¶
AnthropicClient client = AnthropicOkHttpClient.fromEnv();¶

StructuredMessageCreateParams<ContactInfo> createParams = MessageCreateParams.builder()¶
.model(Model.CLAUDE_OPUS_4_6)¶
.maxTokens(1024)¶
.outputConfig(ContactInfo.class)¶
.addUserMessage("Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan and wants to schedule a demo for next Tuesday at 2pm.")¶
.build();¶

StructuredMessage<ContactInfo> response = client.messages().create(createParams);¶
ContactInfo contact = response.content().get(0).asText().text();¶
System.out.println(contact.name + " (" + contact.email + ")");¶
}¶
}¶
```¶

```php PHP hidelines={1..3,6}¶
<?php¶

use Anthropic\Client;¶
use Anthropic\Messages\OutputConfig;¶
use Anthropic\Messages\JSONOutputFormat;¶

$client = new Client();¶

$response = $client->messages->create(¶
maxTokens: 1024,¶
messages: [¶
['role' => 'user', 'content' => 'Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan and wants to schedule a demo for next Tuesday at 2pm.'],¶
],¶
model: 'claude-opus-4-6',¶
outputConfig: OutputConfig::with(format: JSONOutputFormat::with(schema: [¶
'type' => 'object',¶
'properties' => [¶
'name' => ['type' => 'string'],¶
'email' => ['type' => 'string'],¶
'plan_interest' => ['type' => 'string'],¶
'demo_requested' => ['type' => 'boolean'],¶
],¶
'required' => ['name', 'email', 'plan_interest', 'demo_requested'],¶
'additionalProperties' => false,¶
])),¶
);¶

$data = json_decode($response->content[0]->text, true);¶
echo $data['name'] . ' (' . $data['email'] . ')';¶
```¶

```ruby Ruby hidelines={1..2}¶
require "anthropic"¶

client = Anthropic::Client.new¶

class ContactInfo < Anthropic::BaseModel¶
required :name, String¶
required :email, String¶
required :plan_interest, String¶
required :demo_requested, Anthropic::Boolean¶
end¶

message = client.messages.create(¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
messages: [{¶
role: "user",¶
content: "Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan and wants to schedule a demo for next Tuesday at 2pm."¶
}],¶
output_config: {format: ContactInfo}¶
)¶

contact = message.parsed_output¶
puts "#{contact.name} (#{contact.email})"¶
```¶

</CodeGroup>¶

#### SDK-specific methods¶

Each SDK provides helpers that make working with structured outputs easier. See individual SDK pages for full details.¶

<Tabs>¶
<Tab title="Python">¶

**`client.messages.parse()` (Recommended)**¶

The `parse()` method automatically transforms your Pydantic model, validates the response, and returns a `parsed_output` attribute.¶

<section title="Example usage">¶

```python hidelines={2..4,9..12}¶
from pydantic import BaseModel¶
import anthropic¶


class ContactInfo(BaseModel):¶
name: str¶
email: str¶
plan_interest: str¶


client = anthropic.Anthropic()¶

response = client.messages.parse(¶
model="claude-opus-4-6",¶
max_tokens=1024,¶
messages=[¶
{¶
"role": "user",¶
"content": "Extract contact info: John Smith, john@example.com, interested in the Pro plan",¶
}¶
],¶
output_format=ContactInfo,¶
)¶

# Access the parsed output directly¶
contact = response.parsed_output¶
print(contact.name, contact.email)¶
```¶

</section>¶

**`transform_schema()` helper**¶

For when you need to manually transform schemas before sending, or when you want to modify a Pydantic-generated schema. Unlike `client.messages.parse()`, which transforms provided schemas automatically, this gives you the transformed schema so you can further customize it.¶

<section title="Example usage">¶

```python nocheck¶
from anthropic import transform_schema¶
from pydantic import TypeAdapter¶

# First convert Pydantic model to JSON schema, then transform¶
schema = TypeAdapter(ContactInfo).json_schema()¶
schema = transform_schema(schema)¶
# Modify schema if needed¶
schema["properties"]["custom_field"] = {"type": "string"}¶

response = client.messages.create(¶
model="claude-opus-4-6",¶
max_tokens=1024,¶
messages=[{"role": "user", "content": "..."}],¶
output_config={¶
"format": {"type": "json_schema", "schema": schema},¶
},¶
)¶
```¶

</section>¶

</Tab>¶
<Tab title="TypeScript">¶

**`client.messages.parse()` with `zodOutputFormat()`**¶

The `parse()` method accepts a Zod schema, validates the response, and returns a `parsed_output` attribute with the inferred TypeScript type matching the schema.¶

<section title="Example usage">¶

```typescript hidelines={1}¶
import Anthropic from "@anthropic-ai/sdk";¶
import { z } from "zod";¶
import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";¶

const ContactInfo = z.object({¶
name: z.string(),¶
email: z.string(),¶
planInterest: z.string()¶
});¶

const client = new Anthropic();¶

const response = await client.messages.parse({¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
messages: [¶
{¶
role: "user",¶
content: "Extract contact info: John Smith, john@example.com, interested in the Pro plan"¶
}¶
],¶
output_config: { format: zodOutputFormat(ContactInfo) }¶
});¶

// Guaranteed type-safe¶
console.log(response.parsed_output!.email);¶
```¶

</section>¶

</Tab>¶
<Tab title="C#">¶

**Raw JSON schemas via `OutputConfig`**¶

The C# SDK uses raw JSON schemas built programmatically with `JsonSerializer.SerializeToElement`. Deserialize the response JSON with `JsonSerializer.Deserialize`.¶

<section title="Example usage">¶

```csharp¶
using System.Text.Json;¶
using Anthropic;¶
using Anthropic.Models.Messages;¶

var client = new AnthropicClient();¶

var response = await client.Messages.Create(new MessageCreateParams¶
{¶
Model = "claude-opus-4-6",¶
MaxTokens = 1024,¶
Messages = [new() {¶
Role = Role.User,¶
Content = "Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan."¶
}],¶
OutputConfig = new OutputConfig¶
{¶
Format = new JsonOutputFormat¶
{¶
Schema = new Dictionary<string, JsonElement>¶
{¶
["type"] = JsonSerializer.SerializeToElement("object"),¶
["properties"] = JsonSerializer.SerializeToElement(new¶
{¶
name = new { type = "string" },¶
email = new { type = "string" },¶
plan_interest = new { type = "string" },¶
}),¶
["required"] = JsonSerializer.SerializeToElement(¶
new[] { "name", "email", "plan_interest" }),¶
["additionalProperties"] = JsonSerializer.SerializeToElement(false),¶
},¶
},¶
},¶
});¶

var json = (response.Content.First().Value as TextBlock)!.Text;¶
// JSON is guaranteed to match the schema¶
var contact = JsonSerializer.Deserialize<Dictionary<string, object>>(json);¶
Console.WriteLine($"{contact["name"]} ({contact["email"]})");¶
```¶

</section>¶

</Tab>¶
<Tab title="Go">¶

**Raw JSON schemas via `OutputConfigParam`**¶

The Go SDK works with raw JSON schemas. Define a Go struct with json tags, generate the JSON schema (for example, using `invopop/jsonschema`), and unmarshal the response text into your struct.¶

<section title="Example usage">¶

```go hidelines={1..2,4..7,26..28,-1}¶
package main¶

import (¶
"context"¶
"encoding/json"¶
"fmt"¶

"github.com/anthropics/anthropic-sdk-go"¶
"github.com/invopop/jsonschema"¶
)¶

type ContactInfo struct {¶
Name string `json:"name" jsonschema:"description=Full name"`¶
Email string `json:"email" jsonschema:"description=Email address"`¶
PlanInterest string `json:"plan_interest" jsonschema:"description=Plan type"`¶
}¶

func generateSchema(v any) map[string]any {¶
r := jsonschema.Reflector{AllowAdditionalProperties: false, DoNotReference: true}¶
s := r.Reflect(v)¶
b, _ := json.Marshal(s)¶
var m map[string]any¶
json.Unmarshal(b, &m)¶
return m¶
}¶

func main() {¶
client := anthropic.NewClient()¶
schema := generateSchema(&ContactInfo{})¶

message, _ := client.Messages.New(context.TODO(), anthropic.MessageNewParams{¶
Model: anthropic.ModelClaudeOpus4_6,¶
MaxTokens: 1024,¶
Messages: []anthropic.MessageParam{¶
anthropic.NewUserMessage(anthropic.NewTextBlock(¶
"Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan.",¶
)),¶
},¶
OutputConfig: anthropic.OutputConfigParam{¶
Format: anthropic.JSONOutputFormatParam{¶
Schema: schema,¶
},¶
},¶
})¶

for _, block := range message.Content {¶
switch variant := block.AsAny().(type) {¶
case anthropic.TextBlock:¶
var contact ContactInfo¶
json.Unmarshal([]byte(variant.Text), &contact)¶
fmt.Printf("%s (%s)\n", contact.Name, contact.Email)¶
}¶
}¶
}¶
```¶

</section>¶

</Tab>¶
<Tab title="Java">¶

**`outputConfig(Class<T>)` method**¶

Pass a Java class to `outputConfig()` and the SDK automatically derives a JSON schema, validates it, and returns a `StructuredMessageCreateParams<T>`. Access the parsed result via `response.content().get(0).asText().text()`.¶

<section title="Example usage">¶

```java hidelines={1..7}¶
import com.anthropic.client.AnthropicClient;¶
import com.anthropic.client.okhttp.AnthropicOkHttpClient;¶
import com.anthropic.models.messages.MessageCreateParams;¶
import com.anthropic.models.messages.StructuredMessage;¶
import com.anthropic.models.messages.StructuredMessageCreateParams;¶
import com.anthropic.models.messages.Model;¶

class ContactInfo {¶
public String name;¶
public String email;¶
public String planInterest;¶
}¶

public class StructuredOutputExample {¶
public static void main(String[] args) {¶
AnthropicClient client = AnthropicOkHttpClient.fromEnv();¶

StructuredMessageCreateParams<ContactInfo> createParams = MessageCreateParams.builder()¶
.model(Model.CLAUDE_OPUS_4_6)¶
.maxTokens(1024)¶
.outputConfig(ContactInfo.class)¶
.addUserMessage("Extract contact info: John Smith, john@example.com, interested in the Pro plan")¶
.build();¶

StructuredMessage<ContactInfo> response = client.messages().create(createParams);¶
ContactInfo contact = response.content().get(0).asText().text();¶
System.out.println(contact.name + " (" + contact.email + ")");¶
}¶
}¶
```¶

</section>¶

<section title="Generic type erasure">¶

Generic type information for fields is retained in the class's metadata, but generic type erasure applies in other scopes. While a JSON schema can be derived from a `BookList.books` field with type `List<Book>`, a valid JSON schema cannot be derived from a local variable of that same type.¶

If an error occurs while converting a JSON response to a Java class instance, the error message includes the JSON response to assist in diagnosis. If your JSON response may contain sensitive information, avoid logging it directly, or ensure that you redact any sensitive details from the error message.¶

</section>¶

<section title="Local schema validation">¶

Structured outputs support a [subset of the JSON Schema language](/docs/en/build-with-claude/structured-outputs#json-schema-limitations). Schemas are generated automatically from classes to align with this subset. The `outputConfig(Class<T>)` method performs a validation check on the schema derived from the specified class.¶

Key points:¶

- **Local validation** occurs without sending requests to the remote AI model.¶
- **Remote validation** is also performed by the AI model upon receiving the JSON schema.¶
- **Version compatibility**: Local validation may fail while remote validation succeeds if the SDK version is outdated.¶
- **Disabling local validation**: Pass `JsonSchemaLocalValidation.NO` if you encounter compatibility issues:¶

```java hidelines={1..2,4..15,22..23}¶
import com.anthropic.client.AnthropicClient;¶
import com.anthropic.client.okhttp.AnthropicOkHttpClient;¶
import com.anthropic.core.JsonSchemaLocalValidation;¶
import com.anthropic.models.messages.MessageCreateParams;¶
import com.anthropic.models.messages.StructuredMessageCreateParams;¶
import com.anthropic.models.messages.Model;¶

class BookList {¶
public java.util.List<String> books;¶
}¶

public class LocalValidationExample {¶
public static void main(String[] args) {¶
AnthropicClient client = AnthropicOkHttpClient.fromEnv();¶

StructuredMessageCreateParams<BookList> createParams = MessageCreateParams.builder()¶
.model(Model.CLAUDE_OPUS_4_6)¶
.maxTokens(2048)¶
.outputConfig(BookList.class, JsonSchemaLocalValidation.NO)¶
.addUserMessage("List some famous late twentieth century novels.")¶
.build();¶
}¶
}¶
```¶

</section>¶

<section title="Streaming">¶

Structured outputs can also be used with streaming. As responses arrive in stream events, you need to accumulate the full response before deserializing the JSON.¶

Use `BetaMessageAccumulator` to collect the JSON strings from the stream. Once accumulated, call `BetaMessageAccumulator.message(Class<T>)` to convert the accumulated `BetaMessage` into a `StructuredMessage`, which automatically deserializes the JSON into your Java class.¶

</section>¶

<section title="JSON schema properties">¶

When a JSON schema is derived from your Java classes, all properties represented by `public` fields or `public` getter methods are included by default. Non-`public` fields and getter methods are excluded.¶

You can control visibility with annotations:¶

- `@JsonIgnore` excludes a `public` field or getter method¶
- `@JsonProperty` includes a non-`public` field or getter method¶

If you define `private` fields with `public` getter methods, the property name is derived from the getter (e.g., `private` field `myValue` with `public` method `getMyValue()` produces a `"myValue"` property). To use a non-conventional getter name, annotate the method with `@JsonProperty`.¶

Each class must define at least one property for the JSON schema. A validation error occurs if no fields or getter methods can produce schema properties, such as when:¶

- There are no fields or getter methods in the class¶
- All `public` members are annotated with `@JsonIgnore`¶
- All non-`public` members lack `@JsonProperty` annotations¶
- A field uses a `Map` type, which produces an empty `"properties"` field¶

</section>¶

<section title="Annotations (Jackson and Swagger)">¶

You can use Jackson Databind annotations to enrich the JSON schema derived from your Java classes:¶

```java hidelines={4..5,-2..}¶
import com.fasterxml.jackson.annotation.JsonClassDescription;¶
import com.fasterxml.jackson.annotation.JsonIgnore;¶
import com.fasterxml.jackson.annotation.JsonPropertyDescription;¶
import java.util.List;¶

class Person {¶

@JsonPropertyDescription("The first name and surname of the person")¶
public String name;¶

public int birthYear;¶

@JsonPropertyDescription("The year the person died, or 'present' if the person is living.")¶
public String deathYear;¶
}¶

@JsonClassDescription("The details of one published book")¶
class Book {¶

public String title;¶
public Person author;¶

@JsonPropertyDescription("The year in which the book was first published.")¶
public int publicationYear;¶

@JsonIgnore¶
public String genre;¶
}¶

class BookList {¶
public List<Book> books;¶
}¶

public class Example { public static void main(String[] args) {} }¶
```¶

Annotation summary:¶

- `@JsonClassDescription`: Add a description to a class¶
- `@JsonPropertyDescription`: Add a description to a field or getter method¶
- `@JsonIgnore`: Exclude a `public` field or getter from the schema¶
- `@JsonProperty`: Include a non-`public` field or getter in the schema¶

If you use `@JsonProperty(required = false)`, the `false` value is ignored. Anthropic JSON schemas must mark all properties as required.¶

You can also use OpenAPI Swagger 2 `@Schema` and `@ArraySchema` annotations for type-specific constraints:¶

```java hidelines={3..4,-2..}¶
import io.swagger.v3.oas.annotations.media.ArraySchema;¶
import io.swagger.v3.oas.annotations.media.Schema;¶
import java.util.List;¶

class Article {¶

@ArraySchema(minItems = 1)¶
public List<String> authors;¶

public String title;¶

@Schema(format = "date")¶
public String publicationDate;¶

@Schema(minimum = "1")¶
public int pageCount;¶
}¶

public class Example { public static void main(String[] args) {} }¶
```¶

Local validation checks that you haven't used any unsupported constraint keywords, but constraint values aren't validated locally. For example, an unsupported `"format"` value may pass local validation but cause a remote error.¶

If you use both Jackson and Swagger annotations to set the same schema field, the Jackson annotation takes precedence.¶

</section>¶

</Tab>¶
<Tab title="PHP">¶

**Raw JSON schemas via `OutputConfig::with()`**¶

The PHP SDK passes raw JSON schemas as associative arrays via `OutputConfig::with()`. Decode the response with `json_decode()`.¶

<section title="Example usage">¶

```php hidelines={1..3,6}¶
<?php¶

use Anthropic\Client;¶
use Anthropic\Messages\OutputConfig;¶
use Anthropic\Messages\JSONOutputFormat;¶

$client = new Client();¶

$response = $client->messages->create(¶
maxTokens: 1024,¶
messages: [¶
['role' => 'user', 'content' => 'Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan.'],¶
],¶
model: 'claude-opus-4-6',¶
outputConfig: OutputConfig::with(format: JSONOutputFormat::with(schema: [¶
'type' => 'object',¶
'properties' => [¶
'name' => ['type' => 'string'],¶
'email' => ['type' => 'string'],¶
'plan_interest' => ['type' => 'string'],¶
],¶
'required' => ['name', 'email', 'plan_interest'],¶
'additionalProperties' => false,¶
])),¶
);¶

$data = json_decode($response->content[0]->text, true);¶
echo $data['name'] . ' (' . $data['email'] . ')';¶
```¶

</section>¶

</Tab>¶
<Tab title="Ruby">¶

**`output_config: {format: Model}` with `parsed_output`**¶

Define a model class extending `Anthropic::BaseModel` and pass it as the format to `messages.create()`. The response includes a `parsed_output` attribute with a typed Ruby object.¶

<section title="Example usage">¶

```ruby hidelines={1..2}¶
require "anthropic"¶

class ContactInfo < Anthropic::BaseModel¶
required :name, String¶
required :email, String¶
required :plan_interest, String¶
end¶

client = Anthropic::Client.new¶

message = client.messages.create(¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
messages: [¶
{¶
role: "user",¶
content: "Extract contact info: John Smith, john@example.com, interested in the Pro plan"¶
}¶
],¶
output_config: {format: ContactInfo}¶
)¶

contact = message.parsed_output¶
puts "#{contact.name} (#{contact.email})"¶
```¶

</section>¶

<section title="Advanced model features">¶

The Ruby SDK supports additional model definition features for richer schemas:¶

- **`doc:` keyword:** Add descriptions to fields for more informative schema output¶
- **`Anthropic::ArrayOf[T]`:** Typed arrays with `min_length` and `max_length` constraints¶
- **`Anthropic::EnumOf[:a, :b]`:** Enum fields with constrained values¶
- **`Anthropic::UnionOf[T1, T2]`:** Union types mapped to `anyOf`¶

```ruby¶
class FamousNumber < Anthropic::BaseModel¶
required :value, Float¶
optional :reason, String, doc: "why is this number mathematically significant?"¶
end¶

class Output < Anthropic::BaseModel¶
required :numbers, Anthropic::ArrayOf[FamousNumber], min_length: 3, max_length: 5¶
end¶

message = client.messages.create(¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
messages: [{role: "user", content: "give me some famous numbers"}],¶
output_config: {format: Output}¶
)¶

message.parsed_output¶
# => #<Output numbers=[#<FamousNumber value=3.14159... reason="Pi is...">...]>¶
```¶

</section>¶

</Tab>¶
</Tabs>¶

#### How SDK transformation works¶

The Python and TypeScript SDKs automatically transform schemas with unsupported features:¶

1. **Remove unsupported constraints** (e.g., `minimum`, `maximum`, `minLength`, `maxLength`)¶
2. **Update descriptions** with constraint info (e.g., "Must be at least 100"), when the constraint is not directly supported with structured outputs¶
3. **Add `additionalProperties: false`** to all objects¶
4. **Filter string formats** to supported list only¶
5. **Validate responses** against your original schema (with all constraints)¶

This means Claude receives a simplified schema, but your code still enforces all constraints through validation.¶

**Example:** A Pydantic field with `minimum: 100` becomes a plain integer in the sent schema, but the description is updated to "Must be at least 100", and the SDK validates the response against the original constraint.¶

### Common use cases¶

<section title="Data extraction">¶

Extract structured data from unstructured text:¶

<CodeGroup>¶

```python Python nocheck¶
from pydantic import BaseModel¶
from typing import List¶


class Invoice(BaseModel):¶
invoice_number: str¶
date: str¶
total_amount: float¶
line_items: List[dict]¶
customer_name: str¶


response = client.messages.parse(¶
model="claude-opus-4-6",¶
max_tokens=4096,¶
output_format=Invoice,¶
messages=[¶
{"role": "user", "content": f"Extract invoice data from: {invoice_text}"}¶
],¶
)¶
```¶

```typescript TypeScript hidelines={1}¶
import Anthropic from "@anthropic-ai/sdk";¶
import { z } from "zod";¶
import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";¶

const client = new Anthropic();¶

const InvoiceSchema = z.object({¶
invoice_number: z.string(),¶
date: z.string(),¶
total_amount: z.number(),¶
line_items: z.array(z.record(z.string(), z.any())),¶
customer_name: z.string()¶
});¶

const invoiceText = "Invoice #12345, Date: 2024-01-15, Total: $500.00";¶
const response = await client.messages.parse({¶
model: "claude-opus-4-6",¶
max_tokens: 4096,¶
output_config: { format: zodOutputFormat(InvoiceSchema) },¶
messages: [{ role: "user", content: `Extract invoice data from: ${invoiceText}` }]¶
});¶
```¶

```csharp C#¶
using System;¶
using System.Collections.Generic;¶
using System.Text.Json;¶
using System.Threading.Tasks;¶
using Anthropic;¶
using Anthropic.Models.Messages;¶

public class InvoiceExtraction¶
{¶
public class Invoice¶
{¶
public string invoice_number { get; set; }¶
public string date { get; set; }¶
public double total_amount { get; set; }¶
public List<Dictionary<string, object>> line_items { get; set; }¶
public string customer_name { get; set; }¶
}¶

static async Task Main()¶
{¶
AnthropicClient client = new();¶

string invoiceText = "Invoice #12345, Date: 2024-01-15, Total: $500.00";¶

var parameters = new MessageCreateParams¶
{¶
Model = Model.ClaudeOpus4_6,¶
MaxTokens = 4096,¶
OutputConfig = new OutputConfig¶
{¶
Format = new JsonOutputFormat¶
{¶
Schema = new Dictionary<string, JsonElement>¶
{¶
["type"] = JsonSerializer.SerializeToElement("object"),¶
["properties"] = JsonSerializer.SerializeToElement(new¶
{¶
invoice_number = new { type = "string" },¶
date = new { type = "string" },¶
total_amount = new { type = "number" },¶
line_items = new¶
{¶
type = "array",¶
items = new¶
{¶
type = "object",¶
additionalProperties = false,¶
},¶
},¶
customer_name = new { type = "string" },¶
}),¶
["required"] = JsonSerializer.SerializeToElement(new[] { "invoice_number", "date", "total_amount", "line_items", "customer_name" }),¶
["additionalProperties"] = JsonSerializer.SerializeToElement(false),¶
},¶
},¶
},¶
Messages = [new() { Role = Role.User, Content = $"Extract invoice data from: {invoiceText}" }]¶
};¶

var message = await client.Messages.Create(parameters);¶
Console.WriteLine(message);¶
}¶
}¶
```¶

```go Go hidelines={1..11,-1}¶
package main¶

import (¶
"context"¶
"fmt"¶
"log"¶

"github.com/anthropics/anthropic-sdk-go"¶
)¶

func main() {¶
client := anthropic.NewClient()¶

invoiceText := "Invoice #12345, Date: 2024-01-15, Total: $500.00"¶

schema := map[string]any{¶
"type": "object",¶
"additionalProperties": false,¶
"properties": map[string]any{¶
"invoice_number": map[string]any{"type": "string"},¶
"date": map[string]any{"type": "string"},¶
"total_amount": map[string]any{"type": "number"},¶
"line_items": map[string]any{¶
"type": "array",¶
"items": map[string]any{¶
"type": "object",¶
"additionalProperties": false,¶
"properties": map[string]any{¶
"description": map[string]any{"type": "string"},¶
"quantity": map[string]any{"type": "number"},¶
"unit_price": map[string]any{"type": "number"},¶
},¶
"required": []string{"description", "quantity", "unit_price"},¶
},¶
},¶
"customer_name": map[string]any{"type": "string"},¶
},¶
"required": []string{"invoice_number", "date", "total_amount", "line_items", "customer_name"},¶
}¶

response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{¶
Model: anthropic.ModelClaudeOpus4_6,¶
MaxTokens: 4096,¶
OutputConfig: anthropic.OutputConfigParam{¶
Format: anthropic.JSONOutputFormatParam{¶
Schema: schema,¶
},¶
},¶
Messages: []anthropic.MessageParam{¶
anthropic.NewUserMessage(anthropic.NewTextBlock(fmt.Sprintf("Extract invoice data from: %s", invoiceText))),¶
},¶
})¶
if err != nil {¶
log.Fatal(err)¶
}¶
for _, block := range response.Content {¶
switch variant := block.AsAny().(type) {¶
case anthropic.TextBlock:¶
fmt.Println(variant.Text)¶
}¶
}¶
}¶
```¶

```java Java hidelines={1..6,8..10}¶
import com.anthropic.client.AnthropicClient;¶
import com.anthropic.client.okhttp.AnthropicOkHttpClient;¶
import com.anthropic.models.messages.MessageCreateParams;¶
import com.anthropic.models.messages.StructuredMessage;¶
import com.anthropic.models.messages.StructuredMessageCreateParams;¶
import com.anthropic.models.messages.Model;¶
import com.fasterxml.jackson.annotation.JsonProperty;¶
import java.util.List;¶
import java.util.Map;¶

public class InvoiceExtraction {¶
static class LineItem {¶
@JsonProperty("description")¶
public String description;¶

@JsonProperty("quantity")¶
public int quantity;¶

@JsonProperty("unit_price")¶
public double unitPrice;¶
}¶

static class Invoice {¶
@JsonProperty("invoice_number")¶
public String invoiceNumber;¶

@JsonProperty("date")¶
public String date;¶

@JsonProperty("total_amount")¶
public double totalAmount;¶

@JsonProperty("line_items")¶
public List<LineItem> lineItems;¶

@JsonProperty("customer_name")¶
public String customerName;¶
}¶

public static void main(String[] args) {¶
AnthropicClient client = AnthropicOkHttpClient.fromEnv();¶

String invoiceText = "Invoice #12345, Date: 2024-01-15, Total: $500.00";¶

StructuredMessageCreateParams<Invoice> params = MessageCreateParams.builder()¶
.model(Model.CLAUDE_OPUS_4_6)¶
.maxTokens(4096L)¶
.outputConfig(Invoice.class)¶
.addUserMessage("Extract invoice data from: " + invoiceText)¶
.build();¶

StructuredMessage<Invoice> response = client.messages().create(params);¶
System.out.println(response);¶
}¶
}¶
```¶

```php PHP hidelines={1..4}¶
<?php¶

use Anthropic\Client;¶

$client = new Client(apiKey: getenv("ANTHROPIC_API_KEY"));¶

$invoiceText = "Invoice #12345, Date: 2024-01-15, Total: $500.00";¶

$message = $client->messages->create(¶
maxTokens: 4096,¶
messages: [¶
['role' => 'user', 'content' => "Extract invoice data from: $invoiceText"]¶
],¶
model: 'claude-opus-4-6',¶
outputConfig: [¶
'format' => [¶
'type' => 'json_schema',¶
'schema' => [¶
'type' => 'object',¶
'properties' => [¶
'invoice_number' => ['type' => 'string'],¶
'date' => ['type' => 'string'],¶
'total_amount' => ['type' => 'number'],¶
'line_items' => [¶
'type' => 'array',¶
'items' => [¶
'type' => 'object',¶
'additionalProperties' => false¶
]¶
],¶
'customer_name' => ['type' => 'string']¶
],¶
'required' => ['invoice_number', 'date', 'total_amount', 'line_items', 'customer_name'],¶
'additionalProperties' => false¶
]¶
]¶
],¶
);¶

echo $message;¶
```¶

```ruby Ruby hidelines={1..2}¶
require "anthropic"¶

client = Anthropic::Client.new¶

invoice_text = "Invoice #12345, Date: 2024-01-15, Total: $500.00"¶

message = client.messages.create(¶
model: "claude-opus-4-6",¶
max_tokens: 4096,¶
output_config: {¶
format: {¶
type: :json_schema,¶
schema: {¶
type: "object",¶
properties: {¶
invoice_number: { type: "string" },¶
date: { type: "string" },¶
total_amount: { type: "number" },¶
line_items: {¶
type: "array",¶
items: {¶
type: "object",¶
additionalProperties: false¶
}¶
},¶
customer_name: { type: "string" }¶
},¶
required: ["invoice_number", "date", "total_amount", "line_items", "customer_name"],¶
additionalProperties: false¶
}¶
}¶
},¶
messages: [¶
{ role: "user", content: "Extract invoice data from: #{invoice_text}" }¶
]¶
)¶
puts message.content.first.text¶
```¶

</CodeGroup>¶

</section>¶

<section title="Classification">¶

Classify content with structured categories:¶

<CodeGroup>¶

```python Python hidelines={1}¶
from anthropic import Anthropic¶
from pydantic import BaseModel¶
from typing import List¶

client = Anthropic()¶


class Classification(BaseModel):¶
category: str¶
confidence: float¶
tags: List[str]¶
sentiment: str¶


feedback_text = "Great product, but the delivery was slow."¶
response = client.messages.parse(¶
model="claude-opus-4-6",¶
max_tokens=1024,¶
output_format=Classification,¶
messages=[{"role": "user", "content": f"Classify this feedback: {feedback_text}"}],¶
)¶
```¶

```typescript TypeScript hidelines={1}¶
import Anthropic from "@anthropic-ai/sdk";¶
import { z } from "zod";¶
import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";¶

const client = new Anthropic();¶

const ClassificationSchema = z.object({¶
category: z.string(),¶
confidence: z.number(),¶
tags: z.array(z.string()),¶
sentiment: z.string()¶
});¶

const feedbackText = "Great product, but the delivery was slow.";¶
const response = await client.messages.parse({¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
output_config: { format: zodOutputFormat(ClassificationSchema) },¶
messages: [{ role: "user", content: `Classify this feedback: ${feedbackText}` }]¶
});¶
```¶

```csharp C# hidelines={1..7}¶
using System.Collections.Generic;¶
using System.Text.Json;¶
using Anthropic;¶
using Anthropic.Models.Messages;¶

AnthropicClient client = new();¶

string feedbackText = "Great product, fast shipping!";¶

var parameters = new MessageCreateParams¶
{¶
Model = Model.ClaudeOpus4_6,¶
MaxTokens = 1024,¶
Messages = [new() { Role = Role.User, Content = $"Classify this feedback: {feedbackText}" }],¶
OutputConfig = new OutputConfig¶
{¶
Format = new JsonOutputFormat¶
{¶
Schema = new Dictionary<string, JsonElement>¶
{¶
["type"] = JsonSerializer.SerializeToElement("object"),¶
["properties"] = JsonSerializer.SerializeToElement(new¶
{¶
category = new { type = "string" },¶
confidence = new { type = "number" },¶
tags = new { type = "array", items = new { type = "string" } },¶
sentiment = new { type = "string" },¶
}),¶
["required"] = JsonSerializer.SerializeToElement(new[] { "category", "confidence", "tags", "sentiment" }),¶
["additionalProperties"] = JsonSerializer.SerializeToElement(false),¶
},¶
},¶
},¶
};¶

var message = await client.Messages.Create(parameters);¶
Console.WriteLine(message);¶
```¶

```go Go hidelines={1..14,-1}¶
package main¶

import (¶
"context"¶
"encoding/json"¶
"fmt"¶
"log"¶

"github.com/anthropics/anthropic-sdk-go"¶
)¶

func main() {¶
client := anthropic.NewClient()¶

feedbackText := "Great product, fast shipping!"¶

schema := map[string]any{¶
"type": "object",¶
"properties": map[string]any{¶
"category": map[string]any{"type": "string"},¶
"confidence": map[string]any{"type": "number"},¶
"tags": map[string]any{"type": "array", "items": map[string]any{"type": "string"}},¶
"sentiment": map[string]any{"type": "string"},¶
},¶
"required": []string{"category", "confidence", "tags", "sentiment"},¶
"additionalProperties": false,¶
}¶

response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{¶
Model: anthropic.ModelClaudeOpus4_6,¶
MaxTokens: 1024,¶
OutputConfig: anthropic.OutputConfigParam{¶
Format: anthropic.JSONOutputFormatParam{¶
Schema: schema,¶
},¶
},¶
Messages: []anthropic.MessageParam{¶
anthropic.NewUserMessage(anthropic.NewTextBlock(fmt.Sprintf("Classify this feedback: %s", feedbackText))),¶
},¶
})¶
if err != nil {¶
log.Fatal(err)¶
}¶
for _, block := range response.Content {¶
switch variant := block.AsAny().(type) {¶
case anthropic.TextBlock:¶
var result map[string]any¶
json.Unmarshal([]byte(variant.Text), &result)¶
fmt.Println(result)¶
}¶
}¶
}¶
```¶

```java Java hidelines={1..6,8..9}¶
import com.anthropic.client.AnthropicClient;¶
import com.anthropic.client.okhttp.AnthropicOkHttpClient;¶
import com.anthropic.models.messages.MessageCreateParams;¶
import com.anthropic.models.messages.StructuredMessage;¶
import com.anthropic.models.messages.StructuredMessageCreateParams;¶
import com.anthropic.models.messages.Model;¶
import com.fasterxml.jackson.annotation.JsonProperty;¶
import java.util.List;¶

public class ClassificationExample {¶
static class Classification {¶
@JsonProperty("category")¶
public String category;¶

@JsonProperty("confidence")¶
public double confidence;¶

@JsonProperty("tags")¶
public List<String> tags;¶

@JsonProperty("sentiment")¶
public String sentiment;¶
}¶

public static void main(String[] args) {¶
AnthropicClient client = AnthropicOkHttpClient.fromEnv();¶

String feedbackText = "Great product, fast shipping!";¶

StructuredMessageCreateParams<Classification> params = MessageCreateParams.builder()¶
.model(Model.CLAUDE_OPUS_4_6)¶
.maxTokens(1024L)¶
.outputConfig(Classification.class)¶
.addUserMessage("Classify this feedback: " + feedbackText)¶
.build();¶

StructuredMessage<Classification> response = client.messages().create(params);¶
Classification result = response.content().get(0).asText().text();¶
System.out.println(result.category + " (" + result.confidence + ")");¶
}¶
}¶
```¶

```php PHP hidelines={1..4}¶
<?php¶

use Anthropic\Client;¶

$client = new Client(apiKey: getenv("ANTHROPIC_API_KEY"));¶

$feedbackText = "Great product, fast shipping!";¶

$message = $client->messages->create(¶
maxTokens: 1024,¶
messages: [¶
['role' => 'user', 'content' => "Classify this feedback: {$feedbackText}"]¶
],¶
model: 'claude-opus-4-6',¶
outputConfig: [¶
'format' => [¶
'type' => 'json_schema',¶
'schema' => [¶
'type' => 'object',¶
'properties' => [¶
'category' => ['type' => 'string'],¶
'confidence' => ['type' => 'number'],¶
'tags' => ['type' => 'array', 'items' => ['type' => 'string']],¶
'sentiment' => ['type' => 'string']¶
],¶
'required' => ['category', 'confidence', 'tags', 'sentiment'],¶
'additionalProperties' => false¶
]¶
]¶
],¶
);¶
echo $message->content[0]->text;¶
```¶

```ruby Ruby hidelines={1..2}¶
require "anthropic"¶

client = Anthropic::Client.new¶

class Classification < Anthropic::BaseModel¶
required :category, String¶
required :confidence, Float¶
required :tags, Anthropic::ArrayOf[String]¶
required :sentiment, String¶
end¶

feedback_text = "Great product, fast shipping!"¶

message = client.messages.create(¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
output_config: {format: Classification},¶
messages: [¶
{role: "user", content: "Classify this feedback: #{feedback_text}"}¶
]¶
)¶
puts message.parsed_output¶
```¶

</CodeGroup>¶

</section>¶

<section title="API response formatting">¶

Generate API-ready responses:¶

<CodeGroup>¶

```python Python hidelines={1}¶
from anthropic import Anthropic¶
from pydantic import BaseModel¶
from typing import List, Optional¶

client = Anthropic()¶


class APIResponse(BaseModel):¶
status: str¶
data: dict¶
errors: Optional[List[dict]]¶
metadata: dict¶


response = client.messages.parse(¶
model="claude-opus-4-6",¶
max_tokens=1024,¶
output_format=APIResponse,¶
messages=[{"role": "user", "content": "Process this request: ..."}],¶
)¶
```¶

```typescript TypeScript hidelines={1}¶
import Anthropic from "@anthropic-ai/sdk";¶
import { z } from "zod";¶
import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";¶

const client = new Anthropic();¶

const APIResponseSchema = z.object({¶
status: z.string(),¶
data: z.record(z.string(), z.any()),¶
errors: z.array(z.record(z.string(), z.any())).optional(),¶
metadata: z.record(z.string(), z.any())¶
});¶

const response = await client.messages.parse({¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
output_config: { format: zodOutputFormat(APIResponseSchema) },¶
messages: [{ role: "user", content: "Process this request..." }]¶
});¶
```¶

```csharp C# hidelines={1..7}¶
using System.Collections.Generic;¶
using System.Text.Json;¶
using Anthropic;¶
using Anthropic.Models.Messages;¶

AnthropicClient client = new();¶

var parameters = new MessageCreateParams¶
{¶
Model = Model.ClaudeOpus4_6,¶
MaxTokens = 1024,¶
Messages = [new() { Role = Role.User, Content = "Process this request: ..." }],¶
OutputConfig = new OutputConfig¶
{¶
Format = new JsonOutputFormat¶
{¶
Schema = new Dictionary<string, JsonElement>¶
{¶
["type"] = JsonSerializer.SerializeToElement("object"),¶
["properties"] = JsonSerializer.SerializeToElement(new¶
{¶
status = new { type = "string" },¶
data = new { type = "object", additionalProperties = false },¶
errors = new¶
{¶
type = "array",¶
items = new { type = "object", additionalProperties = false },¶
},¶
metadata = new { type = "object", additionalProperties = false },¶
}),¶
["required"] = JsonSerializer.SerializeToElement(new[] { "status", "data", "metadata" }),¶
["additionalProperties"] = JsonSerializer.SerializeToElement(false),¶
},¶
},¶
},¶
};¶

var message = await client.Messages.Create(parameters);¶
Console.WriteLine(message);¶
```¶

```go Go hidelines={1..11,-1}¶
package main¶

import (¶
"context"¶
"fmt"¶
"log"¶

"github.com/anthropics/anthropic-sdk-go"¶
)¶

func main() {¶
client := anthropic.NewClient()¶

response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{¶
Model: anthropic.ModelClaudeOpus4_6,¶
MaxTokens: 1024,¶
OutputConfig: anthropic.OutputConfigParam{¶
Format: anthropic.JSONOutputFormatParam{¶
Schema: map[string]any{¶
"type": "object",¶
"additionalProperties": false,¶
"properties": map[string]any{¶
"status": map[string]any{¶
"type": "string",¶
},¶
"data": map[string]any{¶
"type": "object",¶
"additionalProperties": false,¶
},¶
"errors": map[string]any{¶
"type": "array",¶
"items": map[string]any{¶
"type": "object",¶
"additionalProperties": false,¶
},¶
},¶
"metadata": map[string]any{¶
"type": "object",¶
"additionalProperties": false,¶
},¶
},¶
"required": []string{"status", "data", "metadata"},¶
},¶
},¶
},¶
Messages: []anthropic.MessageParam{¶
anthropic.NewUserMessage(anthropic.NewTextBlock("Process this request: ...")),¶
},¶
})¶
if err != nil {¶
log.Fatal(err)¶
}¶
for _, block := range response.Content {¶
switch variant := block.AsAny().(type) {¶
case anthropic.TextBlock:¶
fmt.Println(variant.Text)¶
}¶
}¶
}¶
```¶

```java Java hidelines={1..6,8..9}¶
import com.anthropic.client.AnthropicClient;¶
import com.anthropic.client.okhttp.AnthropicOkHttpClient;¶
import com.anthropic.models.messages.MessageCreateParams;¶
import com.anthropic.models.messages.StructuredMessage;¶
import com.anthropic.models.messages.StructuredMessageCreateParams;¶
import com.anthropic.models.messages.Model;¶
import com.fasterxml.jackson.annotation.JsonProperty;¶
import java.util.List;¶

public class StructuredOutputExample {¶
static class APIData {¶
@JsonProperty("message")¶
public String message;¶

@JsonProperty("resource_id")¶
public String resourceId;¶
}¶

static class APIError {¶
@JsonProperty("code")¶
public String code;¶

@JsonProperty("message")¶
public String message;¶
}¶

static class APIMetadata {¶
@JsonProperty("request_id")¶
public String requestId;¶

@JsonProperty("timestamp")¶
public String timestamp;¶
}¶

static class APIResponse {¶
@JsonProperty("status")¶
public String status;¶

@JsonProperty("data")¶
public APIData data;¶

@JsonProperty("errors")¶
public List<APIError> errors;¶

@JsonProperty("metadata")¶
public APIMetadata metadata;¶
}¶

public static void main(String[] args) {¶
AnthropicClient client = AnthropicOkHttpClient.fromEnv();¶

StructuredMessageCreateParams<APIResponse> params = MessageCreateParams.builder()¶
.model(Model.CLAUDE_OPUS_4_6)¶
.maxTokens(1024L)¶
.outputConfig(APIResponse.class)¶
.addUserMessage("Process this request: ...")¶
.build();¶

StructuredMessage<APIResponse> response = client.messages().create(params);¶
APIResponse result = response.content().get(0).asText().text();¶
System.out.println(result.status);¶
}¶
}¶
```¶

```php PHP hidelines={1..4}¶
<?php¶

use Anthropic\Client;¶

$client = new Client(apiKey: getenv("ANTHROPIC_API_KEY"));¶

$message = $client->messages->create(¶
maxTokens: 1024,¶
messages: [¶
['role' => 'user', 'content' => 'Process this request: ...']¶
],¶
model: 'claude-opus-4-6',¶
outputConfig: [¶
'format' => [¶
'type' => 'json_schema',¶
'schema' => [¶
'type' => 'object',¶
'properties' => [¶
'status' => ['type' => 'string'],¶
'data' => ['type' => 'object', 'additionalProperties' => false],¶
'errors' => [¶
'type' => 'array',¶
'items' => ['type' => 'object', 'additionalProperties' => false]¶
],¶
'metadata' => ['type' => 'object', 'additionalProperties' => false]¶
],¶
'required' => ['status', 'data', 'metadata'],¶
'additionalProperties' => false¶
]¶
]¶
],¶
);¶

echo $message;¶
```¶

```ruby Ruby hidelines={1..2}¶
require "anthropic"¶

client = Anthropic::Client.new¶

message = client.messages.create(¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
output_config: {¶
format: {¶
type: :json_schema,¶
schema: {¶
type: "object",¶
properties: {¶
status: { type: "string" },¶
data: { type: "object", additionalProperties: false },¶
errors: {¶
type: "array",¶
items: { type: "object", additionalProperties: false }¶
},¶
metadata: { type: "object", additionalProperties: false }¶
},¶
required: ["status", "data", "metadata"],¶
additionalProperties: false¶
}¶
}¶
},¶
messages: [¶
{ role: "user", content: "Process this request: ..." }¶
]¶
)¶
puts message.content.first.text¶
```¶

</CodeGroup>¶

</section>¶

## Strict tool use¶

For enforcing JSON Schema compliance on tool inputs with grammar-constrained sampling, see [Strict tool use](/docs/en/agents-and-tools/tool-use/strict-tool-use).¶

## Using both features together¶

JSON outputs and strict tool use solve different problems and can be used together:¶

- **JSON outputs** control Claude's response format (what Claude says)¶
- **Strict tool use** validates tool parameters (how Claude calls your functions)¶

When combined, Claude can call tools with guaranteed-valid parameters AND return structured JSON responses. This is useful for agentic workflows where you need both reliable tool calls and structured final outputs.¶

<CodeGroup>¶

```python Python¶
response = client.messages.create(¶
model="claude-opus-4-6",¶
max_tokens=1024,¶
messages=[¶
{¶
"role": "user",¶
"content": "Help me plan a trip to Paris departing May 15, 2026",¶
}¶
],¶
# JSON outputs: structured response format¶
output_config={¶
"format": {¶
"type": "json_schema",¶
"schema": {¶
"type": "object",¶
"properties": {¶
"summary": {"type": "string"},¶
"next_steps": {"type": "array", "items": {"type": "string"}},¶
},¶
"required": ["summary", "next_steps"],¶
"additionalProperties": False,¶
},¶
}¶
},¶
# Strict tool use: guaranteed tool parameters¶
tools=[¶
{¶
"name": "search_flights",¶
"strict": True,¶
"input_schema": {¶
"type": "object",¶
"properties": {¶
"destination": {"type": "string"},¶
"date": {"type": "string", "format": "date"},¶
},¶
"required": ["destination", "date"],¶
"additionalProperties": False,¶
},¶
}¶
],¶
)¶
```¶

```typescript TypeScript¶
const response = await client.messages.create({¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
messages: [{ role: "user", content: "Help me plan a trip to Paris departing May 15, 2026" }],¶
// JSON outputs: structured response format¶
output_config: {¶
format: {¶
type: "json_schema",¶
schema: {¶
type: "object",¶
properties: {¶
summary: { type: "string" },¶
next_steps: { type: "array", items: { type: "string" } }¶
},¶
required: ["summary", "next_steps"],¶
additionalProperties: false¶
}¶
}¶
},¶
// Strict tool use: guaranteed tool parameters¶
tools: [¶
{¶
name: "search_flights",¶
description: "Search for available flights to a destination on a specific date",¶
strict: true,¶
input_schema: {¶
type: "object",¶
properties: {¶
destination: { type: "string" },¶
date: { type: "string", format: "date" }¶
},¶
required: ["destination", "date"],¶
additionalProperties: false¶
}¶
}¶
]¶
});¶

// Claude may call the tool first (tool_use) or respond with JSON (text)¶
console.log("Stop reason:", response.stop_reason);¶
for (const block of response.content) {¶
if (block.type === "tool_use") {¶
console.log(`Tool call: ${block.name}(${JSON.stringify(block.input)})`);¶
} else if (block.type === "text") {¶
console.log("Response:", block.text);¶
}¶
}¶
```¶

```csharp C# hidelines={1..7}¶
using System.Collections.Generic;¶
using System.Text.Json;¶
using Anthropic;¶
using Anthropic.Models.Messages;¶

AnthropicClient client = new();¶

var parameters = new MessageCreateParams¶
{¶
Model = Model.ClaudeOpus4_6,¶
MaxTokens = 1024,¶
Messages = [new() { Role = Role.User, Content = "Help me plan a trip to Paris departing May 15, 2026" }],¶
// JSON outputs: structured response format¶
OutputConfig = new OutputConfig¶
{¶
Format = new JsonOutputFormat¶
{¶
Schema = new Dictionary<string, JsonElement>¶
{¶
["type"] = JsonSerializer.SerializeToElement("object"),¶
["properties"] = JsonSerializer.SerializeToElement(new¶
{¶
summary = new { type = "string" },¶
next_steps = new { type = "array", items = new { type = "string" } },¶
}),¶
["required"] = JsonSerializer.SerializeToElement(new[] { "summary", "next_steps" }),¶
["additionalProperties"] = JsonSerializer.SerializeToElement(false),¶
},¶
},¶
},¶
// Strict tool use: guaranteed tool parameters¶
Tools =¶
[¶
new Tool¶
{¶
Name = "search_flights",¶
Strict = true,¶
InputSchema = new InputSchema(new Dictionary<string, JsonElement>¶
{¶
["properties"] = JsonSerializer.SerializeToElement(new Dictionary<string, object>¶
{¶
["destination"] = new { type = "string" },¶
["date"] = new { type = "string", format = "date" },¶
}),¶
["required"] = JsonSerializer.SerializeToElement(new[] { "destination", "date" }),¶
["additionalProperties"] = JsonSerializer.SerializeToElement(false),¶
}),¶
}¶
],¶
};¶

var message = await client.Messages.Create(parameters);¶
Console.WriteLine(message);¶
```¶

```go Go hidelines={1..11,-1}¶
package main¶

import (¶
"context"¶
"fmt"¶
"log"¶

"github.com/anthropics/anthropic-sdk-go"¶
)¶

func main() {¶
client := anthropic.NewClient()¶

response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{¶
Model: anthropic.ModelClaudeOpus4_6,¶
MaxTokens: 1024,¶
Messages: []anthropic.MessageParam{¶
anthropic.NewUserMessage(anthropic.NewTextBlock("Help me plan a trip to Paris departing May 15, 2026")),¶
},¶
// JSON outputs: structured response format¶
OutputConfig: anthropic.OutputConfigParam{¶
Format: anthropic.JSONOutputFormatParam{¶
Schema: map[string]any{¶
"type": "object",¶
"additionalProperties": false,¶
"properties": map[string]any{¶
"summary": map[string]any{"type": "string"},¶
"next_steps": map[string]any{"type": "array", "items": map[string]any{"type": "string"}},¶
},¶
"required": []string{"summary", "next_steps"},¶
},¶
},¶
},¶
// Strict tool use: guaranteed tool parameters¶
Tools: []anthropic.ToolUnionParam{¶
{OfTool: &anthropic.ToolParam{¶
Name: "search_flights",¶
Strict: anthropic.Bool(true),¶
InputSchema: anthropic.ToolInputSchemaParam{¶
Properties: map[string]any{¶
"destination": map[string]any{"type": "string"},¶
"date": map[string]any{"type": "string", "format": "date"},¶
},¶
Required: []string{"destination", "date"},¶
ExtraFields: map[string]any{¶
"additionalProperties": false,¶
},¶
}}},¶
},¶
})¶
if err != nil {¶
log.Fatal(err)¶
}¶
fmt.Println(response.Content)¶
}¶
```¶

```java Java hidelines={1..15,-2..}¶
import com.anthropic.client.AnthropicClient;¶
import com.anthropic.client.okhttp.AnthropicOkHttpClient;¶
import com.anthropic.core.JsonValue;¶
import com.anthropic.models.messages.JsonOutputFormat;¶
import com.anthropic.models.messages.Message;¶
import com.anthropic.models.messages.MessageCreateParams;¶
import com.anthropic.models.messages.Model;¶
import com.anthropic.models.messages.OutputConfig;¶
import com.anthropic.models.messages.Tool;¶
import com.anthropic.models.messages.Tool.InputSchema;¶
import java.util.List;¶
import java.util.Map;¶

public class StructuredOutputExample {¶
public static void main(String[] args) {¶
AnthropicClient client = AnthropicOkHttpClient.fromEnv();¶

// JSON outputs: structured response format¶
JsonOutputFormat.Schema outputSchema = JsonOutputFormat.Schema.builder()¶
.putAdditionalProperty("type", JsonValue.from("object"))¶
.putAdditionalProperty("properties", JsonValue.from(Map.of(¶
"summary", Map.of("type", "string"),¶
"next_steps", Map.of("type", "array", "items", Map.of("type", "string"))¶
)))¶
.putAdditionalProperty("required", JsonValue.from(List.of("summary", "next_steps")))¶
.putAdditionalProperty("additionalProperties", JsonValue.from(false))¶
.build();¶

// Strict tool use: guaranteed tool parameters¶
InputSchema toolSchema = InputSchema.builder()¶
.properties(JsonValue.from(Map.of(¶
"destination", Map.of("type", "string"),¶
"date", Map.of("type", "string", "format", "date")¶
)))¶
.putAdditionalProperty("required", JsonValue.from(List.of("destination", "date")))¶
.putAdditionalProperty("additionalProperties", JsonValue.from(false))¶
.build();¶

MessageCreateParams params = MessageCreateParams.builder()¶
.model(Model.CLAUDE_OPUS_4_6)¶
.maxTokens(1024L)¶
.addUserMessage("Help me plan a trip to Paris departing May 15, 2026")¶
.outputConfig(OutputConfig.builder()¶
.format(JsonOutputFormat.builder().schema(outputSchema).build())¶
.build())¶
.addTool(Tool.builder()¶
.name("search_flights")¶
.description("Search for available flights to a destination on a specific date")¶
.strict(true)¶
.inputSchema(toolSchema)¶
.build())¶
.build();¶

Message response = client.messages().create(params);¶
System.out.println(response);¶
}¶
}¶
```¶

```php PHP hidelines={1..4}¶
<?php¶

use Anthropic\Client;¶

$client = new Client(apiKey: getenv("ANTHROPIC_API_KEY"));¶

$message = $client->messages->create(¶
maxTokens: 1024,¶
messages: [¶
['role' => 'user', 'content' => 'Help me plan a trip to Paris departing May 15, 2026']¶
],¶
model: 'claude-opus-4-6',¶
// JSON outputs: structured response format¶
outputConfig: [¶
'format' => [¶
'type' => 'json_schema',¶
'schema' => [¶
'type' => 'object',¶
'properties' => [¶
'summary' => ['type' => 'string'],¶
'next_steps' => ['type' => 'array', 'items' => ['type' => 'string']]¶
],¶
'required' => ['summary', 'next_steps'],¶
'additionalProperties' => false¶
]¶
]¶
],¶
// Strict tool use: guaranteed tool parameters¶
tools: [¶
[¶
'name' => 'search_flights',¶
'strict' => true,¶
'input_schema' => [¶
'type' => 'object',¶
'properties' => [¶
'destination' => ['type' => 'string'],¶
'date' => ['type' => 'string', 'format' => 'date']¶
],¶
'required' => ['destination', 'date'],¶
'additionalProperties' => false¶
]¶
]¶
],¶
);¶
echo $message;¶
```¶

```ruby Ruby hidelines={1..2}¶
require "anthropic"¶

client = Anthropic::Client.new¶

message = client.messages.create(¶
model: "claude-opus-4-6",¶
max_tokens: 1024,¶
messages: [¶
{role: "user", content: "Help me plan a trip to Paris departing May 15, 2026"}¶
],¶
# JSON outputs: structured response format¶
output_config: {¶
format: {¶
type: :json_schema,¶
schema: {¶
type: "object",¶
properties: {¶
summary: {type: "string"},¶
next_steps: {type: "array", items: {type: "string"}}¶
},¶
required: ["summary", "next_steps"],¶
additionalProperties: false¶
}¶
}¶
},¶
# Strict tool use: guaranteed tool parameters¶
tools: [¶
{¶
name: "search_flights",¶
strict: true,¶
input_schema: {¶
type: "object",¶
properties: {¶
destination: {type: "string"},¶
date: {type: "string", format: "date"}¶
},¶
required: ["destination", "date"],¶
additionalProperties: false¶
}¶
}¶
]¶
)¶
puts message¶
```¶

</CodeGroup>¶

## Important considerations¶

### Grammar compilation and caching¶

Structured outputs use constrained sampling with compiled grammar artifacts. This introduces some performance characteristics to be aware of:¶

- **First request latency:** The first time you use a specific schema, there is additional latency while the grammar compiles¶
- **Automatic caching:** Compiled grammars are cached for 24 hours from last use, making subsequent requests much faster¶
- **Cache invalidation:** The cache is invalidated if you change:¶
- The JSON schema structure¶
- The set of tools in your request (when using both structured outputs and tool use)¶
- Changing only `name` or `description` fields does not invalidate the cache¶

### Prompt modification and token costs¶

When using structured outputs, Claude automatically receives an additional system prompt explaining the expected output format. This means:¶

- Your input token count is slightly higher¶
- The injected prompt costs you tokens like any other system prompt¶
- Changing the `output_config.format` parameter will invalidate any [prompt cache](/docs/en/build-with-claude/prompt-caching) for that conversation thread¶

### JSON Schema limitations¶

Structured outputs support standard JSON Schema with some limitations. Both JSON outputs and strict tool use share these limitations.¶

<section title="Supported features">¶

- All basic types: object, array, string, integer, number, boolean, null¶
- `enum` (strings, numbers, bools, or nulls only - no complex types)¶
- `const`¶
- `anyOf` and `allOf` (with limitations - `allOf` with `$ref` not supported)¶
- `$ref`, `$def`, and `definitions` (external `$ref` not supported)¶
- `default` property for all supported types¶
- `required` and `additionalProperties` (must be set to `false` for objects)¶
- String formats: `date-time`, `time`, `date`, `duration`, `email`, `hostname`, `uri`, `ipv4`, `ipv6`, `uuid`¶
- Array `minItems` (only values 0 and 1 supported)¶

</section>¶

<section title="Not supported">¶

- Recursive schemas¶
- Complex types within enums¶
- External `$ref` (e.g., `'$ref': 'http://...'`)¶
- Numerical constraints (`minimum`, `maximum`, `multipleOf`, etc.)¶
- String constraints (`minLength`, `maxLength`)¶
- Array constraints beyond `minItems` of 0 or 1¶
- `additionalProperties` set to anything other than `false`¶

If you use an unsupported feature, you'll receive a 400 error with details.¶

</section>¶

<section title="Pattern support (regex)">¶

**Supported regex features:**¶
- Full matching (`^...$`) and partial matching¶
- Quantifiers: `*`, `+`, `?`, simple `{n,m}` cases¶
- Character classes: `[]`, `.`, `\d`, `\w`, `\s`¶
- Groups: `(...)`¶

**NOT supported:**¶
- Backreferences to groups (e.g., `\1`, `\2`)¶
- Lookahead/lookbehind assertions (e.g., `(?=...)`, `(?!...)`)¶
- Word boundaries: `\b`, `\B`¶
- Complex `{n,m}` quantifiers with large ranges¶

Simple regex patterns work well. Complex patterns may result in 400 errors.¶

</section>¶

<Tip>¶
The Python and TypeScript SDKs can automatically transform schemas with unsupported features by removing them and adding constraints to field descriptions. See [SDK-specific methods](#sdk-specific-methods) for details.¶
</Tip>¶

### Property ordering¶

When using structured outputs, properties in objects maintain their defined ordering from your schema, with one important caveat: **required properties appear first, followed by optional properties**.¶

For example, given this schema:¶

```json¶
{¶
"type": "object",¶
"properties": {¶
"notes": { "type": "string" },¶
"name": { "type": "string" },¶
"email": { "type": "string" },¶
"age": { "type": "integer" }¶
},¶
"required": ["name", "email"],¶
"additionalProperties": false¶
}¶
```¶

The output will order properties as:¶

1. `name` (required, in schema order)¶
2. `email` (required, in schema order)¶
3. `notes` (optional, in schema order)¶
4. `age` (optional, in schema order)¶

This means the output might look like:¶

```json¶
{¶
"name": "John Smith",¶
"email": "john@example.com",¶
"notes": "Interested in enterprise plan",¶
"age": 35¶
}¶
```¶

If property order in the output is important to your application, ensure all properties are marked as required, or account for this reordering in your parsing logic.¶

### Invalid outputs¶

While structured outputs guarantee schema compliance in most cases, there are scenarios where the output may not match your schema:¶

**Refusals** (`stop_reason: "refusal"`)¶

Claude maintains its safety and helpfulness properties even when using structured outputs. If Claude refuses a request for safety reasons:¶

- The response has `stop_reason: "refusal"`¶
- You'll receive a 200 status code¶
- You'll be billed for the tokens generated¶
- The output may not match your schema because the refusal message takes precedence over schema constraints¶

**Token limit reached** (`stop_reason: "max_tokens"`)¶

If the response is cut off due to reaching the `max_tokens` limit:¶

- The response has `stop_reason: "max_tokens"`¶
- The output may be incomplete and not match your schema¶
- Retry with a higher `max_tokens` value to get the complete structured output¶

### Schema complexity limits¶

Structured outputs work by compiling your JSON schemas into a grammar that constrains Claude's output. More complex schemas produce larger grammars that take longer to compile. To protect against excessive compilation times, the API enforces several complexity limits.¶

#### Explicit limits¶

The following limits apply to all requests with `output_config.format` or `strict: true`:¶

| Limit | Value | Description |¶
|-------|-------|-------------|¶
| Strict tools per request | 20 | Maximum number of tools with `strict: true`. Non-strict tools don't count toward this limit. |¶
| Optional parameters | 24 | Total optional parameters across all strict tool schemas and JSON output schemas. Each parameter not listed in `required` counts toward this limit. |¶
| Parameters with union types | 16 | Total parameters that use `anyOf` or type arrays (e.g., `"type": ["string", "null"]`) across all strict schemas. These are especially expensive because they create exponential compilation cost. |¶

<Note>¶
These limits apply to the combined total across all strict schemas in a single request. For example, if you have 4 strict tools with 6 optional parameters each, you'll reach the 24-parameter limit even though no single tool seems complex.¶
</Note>¶

#### Additional internal limits¶

Beyond the explicit limits above, there are additional internal limits on the compiled grammar size. These limits exist because schema complexity doesn't reduce to a single dimension: features like optional parameters, union types, nested objects, and number of tools interact with each other in ways that can make the compiled grammar disproportionately large.¶

When these limits are exceeded, you'll receive a 400 error with the message "Schema is too complex for compilation." These errors mean the combined complexity of your schemas exceeds what can be efficiently compiled, even if each individual limit above is satisfied. As a final stop-gap, the API also enforces a **compilation timeout of 180 seconds**. Schemas that pass all explicit checks but produce very large compiled grammars may hit this timeout.¶

#### Tips for reducing schema complexity¶

If you're hitting complexity limits, try these strategies in order:¶

1. **Mark only critical tools as strict.** If you have many tools, reserve it for tools where schema violations cause real problems, and rely on Claude's natural adherence for simpler tools.¶

2. **Reduce optional parameters.** Make parameters `required` where possible. Each optional parameter roughly doubles a portion of the grammar's state space. If a parameter always has a reasonable default, consider making it required and having Claude provide that default explicitly.¶

3. **Simplify nested structures.** Deeply nested objects with optional fields compound the complexity. Flatten structures where possible.¶

4. **Split into multiple requests.** If you have many strict tools, consider splitting them across separate requests or sub-agents.¶

For persistent issues with valid schemas, [contact support](https://support.claude.com/en/articles/9015913-how-to-get-support) with your schema definition.¶

## Data retention¶

Prompts and responses are processed with ZDR when using structured outputs. However, the JSON schema itself is temporarily cached for up to 24 hours since last use for optimization purposes. No prompt or response data is retained beyond the API response.¶

Structured outputs are HIPAA eligible, but **PHI must not be included in JSON schema definitions**. The API compiles JSON schemas into grammars that are cached separately from message content, and these cached schemas do not receive the same PHI protections as prompts and responses. Do not include PHI in schema property names, `enum` values, `const` values, or `pattern` regular expressions. PHI should only appear in message content (prompts and responses), where it is protected under HIPAA safeguards.¶

For ZDR and HIPAA eligibility across all features, see [API and data retention](/docs/en/build-with-claude/api-and-data-retention).¶

## Feature compatibility¶

**Works with:**¶
- **[Batch processing](/docs/en/build-with-claude/batch-processing)**: Process structured outputs at scale with 50% discount¶
- **[Token counting](/docs/en/build-with-claude/token-counting)**: Count tokens without compilation¶
- **[Streaming](/docs/en/build-with-claude/streaming)**: Stream structured outputs like normal responses¶
- **Combined usage**: Use JSON outputs (`output_config.format`) and strict tool use (`strict: true`) together in the same request¶

**Incompatible with:**¶
- **[Citations](/docs/en/build-with-claude/citations)**: Citations require interleaving citation blocks with text, which conflicts with strict JSON schema constraints. Returns 400 error if citations enabled with `output_config.format`.¶
- **Message Prefilling**: Incompatible with JSON outputs¶

<Tip>¶
**Grammar scope**: Grammars apply only to Claude's direct output, not to tool use calls, tool results, or thinking tags (when using [Extended Thinking](/docs/en/build-with-claude/extended-thinking)). Grammar state resets between sections, allowing Claude to think freely while still producing structured output in the final response.¶
</Tip>

Unified Diff

--- a/build-with-claude/structured-outputs.md
+++ b/build-with-claude/structured-outputs.md
@@ -12,7 +12,7 @@
 These features can be used independently or together in the same request.
 
 <Note>
-Structured outputs are generally available on the Claude API and Amazon Bedrock for Claude Opus 4.6, Claude Sonnet 4.6, Claude Sonnet 4.5, Claude Opus 4.5, and Claude Haiku 4.5. Structured outputs are in beta on Microsoft Foundry.
+Structured outputs are generally available on the Claude API and Amazon Bedrock for [Claude Mythos Preview](https://anthropic.com/glasswing), Claude Opus 4.6, Claude Sonnet 4.6, Claude Sonnet 4.5, Claude Opus 4.5, and Claude Haiku 4.5. Structured outputs are in beta on Microsoft Foundry. Structured outputs are not supported on Google Cloud's Vertex AI for Claude Mythos Preview.
 </Note>
 
 <Note>