Skip to content

Structured outputs

Structured outputs make the model answer with JSON that follows a JSON Schema you provide, so you can parse the answer instead of scraping text. You ask for it with response_format on Chat Completions and with text.format on the Responses API. Both accept the same three types.

Type Effect
text Plain text. The same as sending nothing.
json_object Free-form JSON, with no schema.
json_schema JSON that follows your schema.

The two dialects spell json_schema differently, as OpenAI does:

// Chat Completions
"response_format": {
"type": "json_schema",
"json_schema": { "name": "invoice", "description": "…", "schema": { … } }
}
// Responses API
"text": {
"format": { "type": "json_schema", "name": "invoice", "description": "…", "schema": { … } }
}
Field Rule
name Required. 1 to 64 letters, digits, _ or -.
schema Required. A JSON Schema object, at most 64,000 characters once serialized.
description Optional. Truncated to 1,000 characters.

Other fields, such as strict, are not read. A malformed format returns 400 invalid_request, with param set to response_format or text.format.

In GET /v1/models, a model that supports structured outputs has belarel.capabilities.structured_output: true and lists response_format in supported_parameters. See Models.

If you ask for json_object or json_schema on a model that cannot produce structured output, the call is refused before any token is generated, so it costs nothing:

{
"error": {
"message": "Model \"<model-id>\" cannot produce structured output",
"type": "invalid_request_error",
"code": "model_lacks_structured_output",
"param": "response_format"
}
}
Terminal window
curl https://api.belarel.com/v1/chat/completions \
-H "Authorization: Bearer $BELAREL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-5",
"messages": [{"role": "user", "content": "Invoice 4512 from Acme, total 1,250.00 USD, due 2026-11-30."}],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "invoice",
"schema": {
"type": "object",
"properties": {
"number": {"type": "string"},
"vendor": {"type": "string"},
"total": {"type": "number"},
"currency": {"type": "string"},
"due_date": {"type": "string"}
},
"required": ["number", "vendor", "total", "currency", "due_date"],
"additionalProperties": false
}
}
}
}'

The JSON arrives as text, where any answer would: choices[0].message.content on Chat Completions, the output_text part of the message on Responses. When you stream, it arrives in content deltas like any text. Concatenate them and parse at the end.