Structured outputs
Constrain answers with JSON Schema and validate the returned values.
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
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
Use a Praxis cloud API key in PRAXIS_API_KEY; see API keys and authentication. Do not put the key in source code. Install Python and uv; TypeScript examples use Bun.
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
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.
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
Save as structured-output.ts. Run bun structured-output.ts; no SDK installation is needed.
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
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.
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
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.