# Structured outputs (/en/docs/developers/model/guides/structured-output)



Structured outputs constrain an answer to a JSON structure. Your application must still check completion, parse JSON, and validate business fields.

## Choose a supported model [#choose-a-supported-model]

Check `supported_parameters` in `GET /openai/v1/models`: candidates should list `response_format` and `structured_outputs`. On 2026-10-12, Chat Completions `json_schema` with `strict` passed for `gpt-5.6-sol`. Messages `output_config` passed for `claude-opus-5`. The examples use these models respectively.

Although `deepseek-flash` lists these parameters, its `json_schema` request returned 400 during testing. It is therefore not used here for schema enforcement. Catalog declarations do not replace response validation. `json_object` (JSON mode) requests JSON formatting, not schema enforcement. Use `json_schema` when fields and types must be constrained.

## 1. Prepare your terminal [#1-prepare-your-terminal]

Use a Praxis cloud API key in `PRAXIS_API_KEY`; see [API keys and authentication](/en/docs/developers/account/api-keys). Do not put the key in source code. Install Python and [uv](https://docs.astral.sh/uv/getting-started/installation/); TypeScript examples use [Bun](https://bun.sh/docs/installation).

```bash
test -n "$PRAXIS_API_KEY"
```

Expected: exit code 0. The examples default to `https://api.deepraxis.com`. To use another environment, set `PRAXIS_API_BASE` to its API origin, without a trailing slash or API path.

Save each Python block using its indicated filename. Run it with `uv run -q --no-project --with openai --with anthropic python <filename>`. Each block is independent.

## 2. Use json\_schema and strict [#2-use-json_schema-and-strict]

Save as `structured-output.py`. Run `uv run -q --no-project --with openai --with anthropic python structured-output.py`.

Set `name`, `strict: true`, and `schema` inside `response_format.json_schema`. This schema makes every property `required` and rejects extra fields with `additionalProperties: false`.

```python
import json
import os
from openai import OpenAI

client = OpenAI(api_key=os.environ['PRAXIS_API_KEY'],
    base_url=os.environ.get('PRAXIS_API_BASE', 'https://api.deepraxis.com') + '/openai/v1')
schema = {'type':'object', 'properties':{
    'city':{'type':'string'}, 'temperature':{'type':'integer'}},
    'required':['city','temperature'], 'additionalProperties':False}
response = client.chat.completions.create(model='gpt-5.6-sol', max_tokens=1024,
    messages=[{'role':'user','content':'Return JSON for Paris with temperature 22.'}],
    response_format={'type':'json_schema','json_schema':{
        'name':'weather','strict':True,'schema':schema}})
print('X-Request-Id:', response._request_id)
choice = response.choices[0]
assert choice.finish_reason == 'stop' and not choice.message.refusal
value = json.loads(choice.message.content)
assert set(value) == {'city','temperature'}
assert isinstance(value['city'], str) and type(value['temperature']) is int
assert value == {'city':'Paris','temperature':22}, value
print(value)
```

Expected: `{'city': 'Paris', 'temperature': 22}`. Exit code: 0. The code does not strip Markdown fences. Only directly parseable JSON with the expected fields and types passes.

Check `finish_reason` and `refusal` before parsing. Do not treat truncated or refused answers as valid business results. For streaming, collect the complete text before parsing.

## 3. Validate the same result with TypeScript [#3-validate-the-same-result-with-typescript]

Save as `structured-output.ts`. Run `bun structured-output.ts`; no SDK installation is needed.

```typescript
import assert from 'node:assert/strict';
const base = process.env.PRAXIS_API_BASE ?? 'https://api.deepraxis.com';
const schema = {type: 'object', properties: {
  city: {type: 'string'}, temperature: {type: 'integer'}},
  required: ['city', 'temperature'], additionalProperties: false};
const response = await fetch(`${base}/openai/v1/chat/completions`, {
  method: 'POST', headers: {'Authorization': `Bearer ${process.env.PRAXIS_API_KEY}`, 'Content-Type': 'application/json'},
  body: JSON.stringify({model: 'gpt-5.6-sol', max_tokens: 1024,
    messages: [{role: 'user', content: 'Return JSON for Paris with temperature 22.'}],
    response_format: {type: 'json_schema', json_schema: {name: 'weather', strict: true, schema}}}),
});
console.log('X-Request-Id:', response.headers.get('x-request-id'));
const data = await response.json();
assert(response.ok, JSON.stringify(data));
const choice = data.choices[0];
assert.equal(choice.finish_reason, 'stop');
assert(!choice.message.refusal);
const value = JSON.parse(choice.message.content);
assert.deepEqual(value, {city: 'Paris', temperature: 22});
console.log(value);
```

Expected: `city=Paris`, integer `temperature=22`, and no extra fields. Exit code: 0.

## 4. Use output\_config with Messages [#4-use-output_config-with-messages]

Messages places the schema inside `output_config.format`, with `type=json_schema`. Do not copy the Chat Completions `response_format` or `json_schema.name` wrapper. Do not add OpenAI's `strict` field here.

Save as `structured-output-anthropic.py`. Run `uv run -q --no-project --with openai --with anthropic python structured-output-anthropic.py`.

```python
import json
import os
from anthropic import Anthropic

client = Anthropic(api_key=os.environ['PRAXIS_API_KEY'],
    base_url=os.environ.get('PRAXIS_API_BASE', 'https://api.deepraxis.com') + '/anthropic')
schema = {'type':'object', 'properties':{
    'city':{'type':'string'}, 'temperature':{'type':'integer'}},
    'required':['city','temperature'], 'additionalProperties':False}
raw = client.messages.with_raw_response.create(model='claude-opus-5', max_tokens=1024,
    messages=[{'role':'user','content':'Return JSON for Paris with temperature 22.'}],
    output_config={'format':{'type':'json_schema','schema':schema}})
print('X-Request-Id:', raw.headers.get('x-request-id'))
message = raw.parse()
assert message.stop_reason == 'end_turn'
value = json.loads(''.join(block.text for block in message.content if block.type == 'text'))
assert set(value) == {'city','temperature'}
assert isinstance(value['city'], str) and type(value['temperature']) is int
assert value == {'city':'Paris','temperature':22}, value
print(value)
```

Expected: `stop_reason=end_turn`, successful JSON validation, and the same object. Exit code: 0.

## Observed failure and fix [#observed-failure-and-fix]

The schema request to `deepseek-flash` returned HTTP 400. The fixed portion of the error message was:

> This response\_format type is unavailable now

The message also includes that request's identifier. Switching to the verified `gpt-5.6-sol` example passed. HTTP 200, JSON inside Markdown, or parseable ordinary JSON alone does not establish schema enforcement. Keep the complete validation shown above.

Protocol details：[OpenAI](https://developers.openai.com/api/docs/guides/structured-outputs) · [Anthropic](https://platform.claude.com/docs/en/build-with-claude/structured-outputs)。

[API reference →](/en/docs/api/compatible/openai) · [Cookbook →](/en/docs/cookbook)
