Chat completions

Structured outputs

Use JSON mode to get structured outputs from supported chat models.

Why structured outputs

Standard chat models return plain text, which is hard to parse if your app needs to read specific fields from the response.

Supported models can return JSON that conforms to any schema you supply, so you can read the output directly in code without retries or fragile parsing. Pass the schema in the response_format key on the chat completions request.

Supported models

Two modes are available. json_schema constrains the output to a JSON Schema you provide. json_object only guarantees valid JSON, with the shape left to your prompt.

ModelAPI idjson_schemajson_object
DeepSeek V4 Flashdeepseek4-flashYesYes
DeepSeek V4 Prodeepseek-v4-proYesYes
GLM 5.3glm5.3YesYes
GLM 5.3 Flashglm5.3-flashYesYes
Qwen3.8 27Bqwen3.8-27bYesYes
Reka Edge 2603reka-edge-2603YesNot declared
Reka Flash 3reka-flash-3YesNot declared

json_object is not declared for reka-edge-2603 and reka-flash-3; use json_schema there for guaranteed support. The feed at GET /v1/models is authoritative. See the model catalog for the current list.

Basic example

Pass a transcript of a voice note to a model and ask it to return a summary in this shape:

JSON
{
  "title": "A title for the voice note",
  "summary": "A short one-sentence summary of the voice note",
  "actionItems": ["Action item 1", "Action item 2"]
}

To enforce the structure, give the model a JSON Schema. Writing JSON Schema by hand is tedious, so use a helper library: Pydantic in Python, Zod in TypeScript.

Pass the schema via the response_format key. Decoding is constrained to the schema, so you don’t need to describe the JSON in the prompt:

import os
from openai import OpenAI
from pydantic import BaseModel, Field

client = OpenAI(
    base_url="https://api.reka.ai/v1",
    api_key=os.environ["REKA_API_KEY"],
)


class VoiceNote(BaseModel):
    title: str = Field(description="A title for the voice note")
    summary: str = Field(description="A short one sentence summary of the voice note.")
    actionItems: list[str] = Field(description="A list of action items from the voice note")


schema = VoiceNote.model_json_schema()

transcript = "Good morning! It's 7:00 AM, and I'm just waking up. Today is going to be a busy day, so let's get started. First, I need to make a quick breakfast. I think I'll have some scrambled eggs and toast with a cup of coffee. While I'm cooking, I'll also check my emails to see if there's anything urgent."

response = client.chat.completions.create(
    model="deepseek4-flash",
    messages=[
        {
            "role": "system",
            "content": "The following is a voice message transcript.",
        },
        {"role": "user", "content": transcript},
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {"name": "voice_note", "schema": schema},
    },
)

note = VoiceNote.model_validate_json(response.choices[0].message.content)
print(note)

The request shape is {"type": "json_schema", "json_schema": {"name": ..., "schema": ...}}. name is required: omitting it returns a 400. The model responds with output that matches the schema:

JSON
{
  "title": "Morning Routine",
  "summary": "Starting the day with a quick breakfast and checking emails",
  "actionItems": [
    "Cook scrambled eggs and toast",
    "Brew a cup of coffee",
    "Check emails for urgent messages"
  ]
}

Schema limitations

The structured-output backend rejects a few JSON Schema keywords with a 400 (response_format json_schema keyword 'uniqueItems' is not supported by this model's structured-output backend):

  • uniqueItems
  • contains
  • minContains
  • maxContains
  • propertyNames

Pydantic emits uniqueItems for set[str] fields; use list[str] instead and deduplicate in code.

JSON object mode

When you only need valid JSON and the exact shape can be left to the prompt, set response_format to {"type": "json_object"}. The model is guaranteed to return a parseable object, but the keys and types are up to your instructions, so describe them clearly in the system prompt.

import json
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.reka.ai/v1",
    api_key=os.environ["REKA_API_KEY"],
)

response = client.chat.completions.create(
    model="deepseek4-flash",
    messages=[
        {
            "role": "system",
            "content": "Extract the city and country from the user's message. Reply only with a JSON object with keys city and country.",
        },
        {"role": "user", "content": "I just landed in Lisbon for the week!"},
    ],
    response_format={"type": "json_object"},
)

data = json.loads(response.choices[0].message.content)
print(data["city"], data["country"])

json_object is not declared for reka-edge-2603 or reka-flash-3; use json_schema there for guaranteed support. Valid JSON is guaranteed by constrained decoding, but a response can still be truncated if it hits max_tokens. See Troubleshooting.

With reasoning and vision

Structured outputs work with reasoning models too. The model thinks first, in message.reasoning_content, and the JSON lands in message.content. The gateway reserves a slice of max_tokens for the JSON, so thinking can’t consume the whole budget. See Reasoning for details.

You can also combine structured outputs with image inputs to extract typed data from pictures: add an image_url block to the user message as shown in Analyze an image, and pass response_format as above.

Troubleshooting

With response_format set, decoding is grammar-constrained, so the output is either valid JSON or truncated; nothing else.

Token limits. The model can run out of output budget mid-structure. Check the max_tokens you’re sending against the model’s ceiling, and watch for a finish_reason of length in the response. If the model truncates, the JSON is incomplete (unterminated strings, missing closing brackets) regardless of how good your schema is. Either raise max_tokens or simplify the schema.

400 on the request. A missing json_schema.name or an unsupported schema keyword is rejected before generation starts. Fix the schema and retry.

Without response_format, generation is unconstrained and the model follows any example JSON in your prompt literally, syntax errors included. If you ask for JSON by prompt alone, validate the examples you embed.

Next steps