> ## Documentation Index
> Fetch the complete documentation index at: https://docs.anannas.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Structured Outputs

> JSON Schema validation for model responses

## Overview

Structured outputs enforce JSON Schema validation on model responses, ensuring consistent, type-safe outputs. This eliminates parsing errors and hallucinated fields, simplifying downstream integrations.

## Request Format

Use `response_format` with `type: "json_schema"`:

```json theme={null}
{
  "model": "openai/gpt-5-mini",
  "messages": [
    {"role": "user", "content": "What's the weather like in London?"}
  ],
  "response_format": {
    "type": "json_schema",
    "json_schema": {
      "name": "weather",
      "strict": true,
      "schema": {
        "type": "object",
        "properties": {
          "location": {
            "type": "string",
            "description": "City or location name"
          },
          "temperature": {
            "type": "number",
            "description": "Temperature in Celsius"
          },
          "conditions": {
            "type": "string",
            "description": "Weather conditions description"
          }
        },
        "required": ["location", "temperature", "conditions"],
        "additionalProperties": false
      }
    }
  }
}
```

## Response Format

The model returns JSON matching your schema:

```json theme={null}
{
  "choices": [{
    "message": {
      "content": "{\"location\": \"London\", \"temperature\": 18, \"conditions\": \"Partly cloudy with light drizzle\"}"
    }
  }]
}
```

Parse the content as JSON:

```python theme={null}
import json

response = client.chat.completions.create(...)
content = response.choices[0].message.content
weather_data = json.loads(content)
```

## Schema Configuration

### `name`

Schema identifier (required):

```json theme={null}
{
  "name": "weather_response"
}
```

### `strict`

Enforce strict validation (default: `false`):

* `true`: Reject responses that don't match schema exactly
* `false`: Allow minor deviations

### `schema`

JSON Schema object defining the structure:

```json theme={null}
{
  "type": "object",
  "properties": {
    "field": {"type": "string"}
  },
  "required": ["field"],
  "additionalProperties": false
}
```

## JSON Schema Support

Supported JSON Schema features:

* Basic types: `string`, `number`, `integer`, `boolean`, `array`, `object`
* Constraints: `enum`, `minLength`, `maxLength`, `minimum`, `maximum`
* Nested objects and arrays
* `required` fields
* `additionalProperties` control

## Model Support

<Card title="View Structured Output Models" icon="book">
  For a complete list of models supporting structured outputs, visit [anannas.ai/models](https://anannas.ai/models) and filter by `json_mode` capability.
</Card>

Structured outputs are supported on:

* **OpenAI**: GPT-4, GPT-4 Turbo, GPT-5 Mini, GPT-5, o1, o3
* **Anthropic**: Claude 3 Opus, Claude 3 Sonnet, Claude Sonnet 4.5
* **Other providers**: Check `/v1/models` for `json_mode` capability

Query available models:

```bash theme={null}
curl https://api.anannas.ai/v1/models \
  -H "Authorization: Bearer $ANANNAS_API_KEY" | \
  jq '.data[] | select(.capabilities[] | contains("json_mode"))'
```

## Best Practices

1. **Property descriptions**: Guide the model with clear descriptions
2. **Use strict mode**: Set `strict: true` for production
3. **Keep schemas simple**: Simpler schemas yield more reliable results
4. **Required fields**: Explicitly mark required fields
5. **Type constraints**: Use `enum` for limited options

## Example: Type-Safe Response

```python theme={null}
from openai import OpenAI
import json

client = OpenAI(
    base_url="https://api.anannas.ai/v1",
    api_key="<ANANNAS_API_KEY>"
)

response = client.chat.completions.create(
    model="openai/gpt-5-mini",
    messages=[
        {"role": "user", "content": "Extract user info from: John Doe, age 30, email john@example.com"}
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "user_info",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"},
                    "age": {"type": "integer"},
                    "email": {"type": "string"}
                },
                "required": ["name", "age", "email"],
                "additionalProperties": False
            }
        }
    }
)

content = response.choices[0].message.content
user_data = json.loads(content)
# Type-safe: user_data["name"], user_data["age"], user_data["email"]
```

## Streaming with Structured Outputs

Structured outputs work with streaming:

```json theme={null}
{
  "stream": true,
  "response_format": {
    "type": "json_schema",
    "json_schema": {
      "name": "weather",
      "strict": true,
      "schema": {...}
    }
  }
}
```

Accumulate content deltas and parse the complete JSON when the stream completes.

## Error Handling

Common errors:

1. **Unsupported model**: Error with `unsupported_parameter` type
2. **Invalid schema**: Request rejected before completion
3. **Schema violation**: Response fails validation in strict mode

```python theme={null}
try:
    response = client.chat.completions.create(...)
    content = json.loads(response.choices[0].message.content)
except json.JSONDecodeError:
    print("Failed to parse structured output")
except Exception as e:
    if "unsupported_parameter" in str(e):
        print("Model does not support structured outputs")
```

## Comparison: JSON Object vs JSON Schema

### JSON Object Mode

Simple JSON output without validation:

```json theme={null}
{
  "response_format": {
    "type": "json_object"
  }
}
```

### JSON Schema Mode

Strict validation with schema:

```json theme={null}
{
  "response_format": {
    "type": "json_schema",
    "json_schema": {
      "name": "response",
      "strict": true,
      "schema": {...}
    }
  }
}
```

## See Also

* [API Overview](/API/Overview) - Request format
* [Parameters](/API/Parameters) - `response_format` parameter
