Skip to main content
Models

Structured output

Make the model return valid JSON, and use JSON Schema to constrain the output structure precisely

When performing information extraction or structured data generation, a model may return extra text (such as a ```json wrapper) that breaks downstream parsing. Enabling structured output ensures the model returns a valid JSON string. JSON Schema mode goes further and gives you precise control over the output structure and types, eliminating extra validation or retries.

Two modes

FeatureJSON Object modeJSON Schema mode
Outputs valid JSONYesYes
Strictly follows schemaNoYes
Supported modelsMost Qwen models, Kimi, GLM, DeepSeekSelected models only
response_format setting{"type": "json_object"}{"type": "json_schema", "json_schema": {...}, "strict": true}
Prompt requirementMust include "JSON"Recommended to describe explicitly
Use caseFlexible JSON outputPrecise schema validation
JSON Object mode ensures the output is a valid JSON string, but does not guarantee a specific structure. To use it:
  1. Set response_format in the request body to {"type": "json_object"}.
  2. Include the word "JSON" (case-insensitive) in the system message or user message. Otherwise the API returns: 'messages' must contain the word 'json' in some form, to use 'response_format' of type 'json_object'.
JSON Schema mode ensures the output conforms to a specified structure. Set response_format to {"type": "json_schema", "json_schema": {...}, "strict": true}.
JSON Schema mode does not require the "JSON" keyword in the prompt.

Supported models

JSON Object

Qwen

Text generation models
  • Qwen-Max: Qwen3.8-Max series, Qwen3.7-Max series
  • Qwen-Max (non-thinking mode): Qwen3.6-Max series, Qwen3-Max series, Qwen-Max series
  • Qwen-Plus: Qwen3.7-Plus series
  • Qwen-Plus (non-thinking mode): Qwen3.6-Plus series, Qwen3.5-Plus series, Qwen-Plus series
  • Qwen-Flash: Qwen3.7-Flash series
  • Qwen-Flash (non-thinking mode): Qwen3.6-Flash series, Qwen3.5-Flash series, Qwen-Flash series
  • Qwen-Turbo (non-thinking mode): Qwen-Turbo series
  • Qwen-Coder: Qwen3-Coder series
  • Qwen-Long: Qwen-Long series
  • Qwen3.8 open-source series
  • Open-source series (non-thinking mode): Qwen3.6 open-source series, Qwen3.5 open-source series, Qwen3 open-source series
  • Open-source series: Qwen3.8 open-source series (qwen3.8-2.4t-a95b), Qwen3-Coder open-source series, Qwen2.5 open-source series (excluding math and coder models)
Multimodal models (non-thinking mode)
  • Qwen-VL: Qwen3-VL-Plus series, Qwen3-VL-Flash series, Qwen-VL-Max series (excluding latest and snapshot versions), Qwen-VL-Plus series (excluding latest and snapshot versions)
  • Qwen-Omni: Qwen3.5-Omni-Plus series
  • Open-source series: Qwen3-VL open-source series

Kimi

  • kimi-k2-thinking

GLM

  • glm-5.1
  • Non-thinking mode: glm-5, glm-4.7, glm-4.6

DeepSeek

  • deepseek-v4-pro-0813, deepseek-v4-pro, deepseek-v4-flash
Models labeled "non-thinking mode" accept response_format set to {"type": "json_object"} in thinking mode without error, but structured output may not take effect. To reliably get valid JSON from these models in thinking mode, see the FAQ.

JSON Schema

Qwen3.8-Max series, Qwen3.7-Max series, and Qwen3.7-Plus series. More models are coming soon.

Getting started

This example extracts structured information from a personal profile using JSON Object mode.
Before calling, obtain an API key and export it as an environment variable. To call through the OpenAI SDK or DashScope SDK, install the SDK first.
  • OpenAI compatible
  • DashScope
from openai import OpenAI
import os

client = OpenAI(
  # If you have not configured the environment variable, replace the next line with: api_key="sk-xxx"
  api_key=os.getenv("DASHSCOPE_API_KEY"),
  base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
)

completion = client.chat.completions.create(
  model="qwen3.8-max",
  messages=[
    {"role": "system", "content": "Extract the user's name and age, and return them in JSON format"},
    {"role": "user", "content": "Hi everyone, my name is Alex Brown, I'm 34 years old, my email is alexbrown@example.com, and I enjoy playing basketball and traveling"},
  ],
  response_format={"type": "json_object"},
)
json_string = completion.choices[0].message.content
print(json_string)
SDK response:
{
  "name": "Alex Brown",
  "age": 34
}
{
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "{\"name\":\"Alex Brown\",\"age\":34}"
      },
      "finish_reason": "stop",
      "index": 0,
      "logprobs": null
    }
  ],
  "object": "chat.completion",
  "usage": {
    "prompt_tokens": 207,
    "completion_tokens": 20,
    "total_tokens": 227,
    "prompt_tokens_details": {
      "cached_tokens": 0
    }
  },
  "created": 1756455080,
  "system_fingerprint": null,
  "model": "qwen3.8-max",
  "id": "chatcmpl-624b665b-fb93-99e7-9ebd-bb6d86d314d2"
}

Extract structured data from images and video

Multimodal models support structured output for images and video. Use JSON mode to pull structured data out of visual content, such as field values from receipts, object locations in images, or events in video. The following example extracts ticket and invoice fields from a scanned receipt.
For image and video file limits, see Image and video understanding.
  • OpenAI compatible
  • DashScope
import os
from openai import OpenAI

client = OpenAI(
  api_key=os.getenv("DASHSCOPE_API_KEY"),
  base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
)

completion = client.chat.completions.create(
  model="qwen3-vl-plus",
  messages=[
    {
      "role": "system",
      "content": [{"type": "text", "text": "You are a helpful assistant."}],
    },
    {
      "role": "user",
      "content": [
        {
          "type": "image_url",
          "image_url": {
            "url": "http://duguang-labelling.oss-cn-shanghai.aliyuncs.com/demo_ocr/receipt_zh_demo.jpg"
          },
        },
        {"type": "text", "text": "Extract ticket (array type, including travel_date, trains, seat_num, arrival_site, price) and invoice information (array type, including invoice_code and invoice_number) from the image. Output a JSON containing both ticket and invoice arrays"},
      ],
    },
  ],
  response_format={"type": "json_object"},
)
json_string = completion.choices[0].message.content
print(json_string)
Response:
{
  "ticket": [
    {
      "travel_date": "2013-06-29",
      "trains": "Liushui",
      "seat_num": "371",
      "arrival_site": "Development Zone",
      "price": "8.00"
    }
  ],
  "invoice": [
    {
      "invoice_code": "221021325353",
      "invoice_number": "10283819"
    }
  ]
}

Optimize prompts

Ambiguous prompts like "return user information" lead to unpredictable output structures. For reliable results, describe the expected schema in your prompt: specify field names, types, required vs. optional status, format constraints (such as date format), and include examples. The system prompt below does all of this. It constrains field types, distinguishes required from optional fields, and uses four examples to show that the hobby field is omitted entirely when hobbies are not mentioned.
System prompt
Extract personal information from the user input and output it in the specified JSON Schema format:

[Output format requirements]
The output must strictly follow this JSON structure:
{
  "info": {
    "name": "string type, required field, user's name",
    "age": "string type, required field, format 'number years old', e.g., '25 years old'",
    "email": "string type, required field, standard email format, e.g., 'user@example.com'"
  },
  "hobby": ["string array type, optional field, contains all user hobbies; omit entirely if not mentioned"]
}

[Field extraction rules]
1. name: Identify the user's name from the text, must extract
2. age: Identify age information, convert to 'number years old' format, must extract
3. email: Identify email address, keep original format, must extract
4. hobby: Identify user hobbies, output as string array; omit hobby field entirely if hobbies are not mentioned

[Reference examples]
Example 1 (with hobby):
Q: My name is Alice, I'm 25 years old, my email is alice@example.com, and my hobby is singing
A: {"info":{"name":"Alice","age":"25 years old","email":"alice@example.com"},"hobby":["singing"]}
Example 2 (with multiple hobbies):
Q: My name is Bob, I'm 30 years old, my email is bob@example.com, and I enjoy dancing and swimming
A: {"info":{"name":"Bob","age":"30 years old","email":"bob@example.com"},"hobby":["dancing","swimming"]}
Example 3 (without hobby):
Q: My name is Dave, I'm 28 years old, and my email is dave@example.com
A: {"info":{"name":"Dave","age":"28 years old","email":"dave@example.com"}}
Example 4 (without hobby):
Q: I'm Sun Qi, 35 years old, and my email is sunqi@example.com
A: {"info":{"name":"Sun Qi","age":"35 years old","email":"sunqi@example.com"}}

Extract information and output JSON strictly according to the above format and rules. Do not include the hobby field if the user doesn't mention hobbies.
Pass it as the system message:
  • OpenAI compatible
  • DashScope
from openai import OpenAI
import os

client = OpenAI(
  api_key=os.getenv("DASHSCOPE_API_KEY"),
  base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
)

system_prompt = """..."""  # Replace with the system prompt above

completion = client.chat.completions.create(
  model="qwen3.8-max",
  messages=[
    {"role": "system", "content": system_prompt},
    {"role": "user", "content": "Hi everyone, my name is Alex Brown, I'm 34 years old, my email is alexbrown@example.com, and I enjoy playing basketball and traveling"},
  ],
  response_format={"type": "json_object"},
)
print(completion.choices[0].message.content)
Response:
{
  "info": {
    "name": "Alex Brown",
    "age": "34 years old",
    "email": "alexbrown@example.com"
  },
  "hobby": ["Basketball", "Traveling"]
}

Constrain output precisely with JSON Schema

JSON Object mode only guarantees that the output is valid JSON — field names, types, and nesting may still differ from what you expect. For automated parsing, API interoperability, and other cases that need strict type constraints, set type to json_schema. The model then follows the schema exactly. The response_format structure looks like this:
{
  "type": "json_schema",
  "json_schema": {
    "name": "schema_name",       // Name of the schema
    "strict": true,              // Recommended: strictly follow the format
    "schema": {
      "type": "object",
      "properties": {...},       // Define field structure
      "required": [...],         // List of required fields
      "additionalProperties": false  // Recommended: only output defined fields
    }
  }
}
This example forces the model to output a JSON object with the required fields name and age, plus the optional field email.

How to use

The OpenAI SDK's parse method accepts a Python Pydantic class or a Node.js Zod object directly and converts it to a JSON Schema for you, so you never write the schema by hand. With the DashScope SDK, construct the JSON Schema manually using the format above.
  • OpenAI compatible
  • DashScope
from pydantic import BaseModel, Field
from openai import OpenAI
import os

client = OpenAI(
  api_key=os.getenv("DASHSCOPE_API_KEY"),
  base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
)

class UserInfo(BaseModel):
  name: str = Field(description="User name")
  age: int = Field(description="User age in years")

completion = client.chat.completions.parse(
  model="qwen3.8-max",
  messages=[
    {"role": "system", "content": "Extract name and age information."},
    {"role": "user", "content": "My name is Liu Wu, I'm 25 years old."},
  ],
  response_format=UserInfo,          # <-- pass the Pydantic class directly
)
result = completion.choices[0].message.parsed
print(f"Name: {result.name}, Age: {result.age}")
Response:
Name: Liu Wu, Age: 25

Configuration guide

List required fields in the required array and leave optional fields out:
{
  "properties": {
    "name": {"type": "string"},
    "age": {"type": "integer"},
    "email": {"type": "string"}
  },
  "required": ["name", "age"]
}
If the input does not provide email information, the output omits the field.
Besides leaving a field out of required, you can allow the null type:
{
  "properties": {
    "name": {"type": "string"},
    "email": {"type": ["string", "null"]}  // Can be string or null
  },
  "required": ["name", "email"]  // Both in required
}
The output then always includes the email field, but its value may be null.
Controls whether the model may output fields not defined in the schema:
{
  "properties": {"name": {"type": "string"}},
  "required": ["name"],
  "additionalProperties": true  // Allow extra fields
}
For the input "I'm Zhang San, 25 years old", the output is {"name": "Zhang San", "age": 25} — including the undefined age field.
ValueBehaviorUse case
falseOnly output defined fieldsPrecise structure control
trueAllow extra fieldsCapture more information
string, number, integer, boolean, object, array, enum.

Going live

Validate before passing downstream In JSON Object mode the output is guaranteed to be valid JSON, but not to match your business schema. Validate it with a library such as jsonschema (Python), Ajv (JavaScript), or Everit (Java) before handing it to downstream services, so missing fields or type errors don't cause parsing failures, data loss, or broken business logic. On failure, retry the request or have a model rewrite the output. Do not set max_tokens Leave max_tokens unset when structured output is enabled. This parameter caps the number of output tokens (the default is the model's maximum) and can truncate the JSON string mid-output, producing invalid JSON. Use the SDK to generate schemas Let the SDK generate the schema. This avoids errors from hand-maintained schemas and gives you automatic validation plus a type-safe parsed result.
from pydantic import BaseModel, Field
from typing import Optional
from openai import OpenAI
import os

client = OpenAI(
  api_key=os.getenv("DASHSCOPE_API_KEY"),
  base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
)

class UserInfo(BaseModel):
  name: str = Field(description="User name")
  age: int = Field(description="User age")
  email: Optional[str] = None        # Optional field

completion = client.chat.completions.parse(
  model="qwen3.8-max",
  messages=[
    {"role": "system", "content": "Extract name and age information."},
    {"role": "user", "content": "My name is Liu Wu, I'm 25 years old."},
  ],
  response_format=UserInfo,          # Pass the Pydantic model directly
)
result = completion.choices[0].message.parsed   # Type-safe parsed result
print(f"Name: {result.name}, Age: {result.age}")

FAQ

Models labeled "non-thinking mode" in Supported models may return content that is not strictly valid JSON when thinking mode is on. Use a two-step approach: first call the thinking model to get high-quality output, then pass any malformed JSON through a model that supports JSON Object mode to fix it.Step 1: Get the output from thinking mode
Setting response_format to {"type": "json_object"} with thinking mode enabled does not cause an error. The example below is a fallback that intentionally omits response_format; use it only to demonstrate the two-step repair when a model's output is not valid JSON.
completion = client.chat.completions.create(
  model="qwen3.8-max",
  messages=[
    {"role": "system", "content": system_prompt},
    {
      "role": "user",
      "content": "Hi everyone, my name is Alex Brown, I'm 34 years old, my email is alexbrown@example.com, and I enjoy playing basketball and traveling",
    },
  ],
  # Enable thinking mode; this fallback example omits response_format (setting it directly does not cause an error)
  extra_body={"enable_thinking": True},
  # Streaming output is required in thinking mode
  stream=True,
)
# Accumulate the model-generated JSON result
json_string = ""
for chunk in completion:
  if not chunk.choices:
    continue
  if chunk.choices[0].delta.content is not None:
    json_string += chunk.choices[0].delta.content
Step 2: Validate and repair the outputTry to parse the json_string from the previous step. If it is valid JSON, use it directly. If not, call a model that supports structured output to repair it — pick a fast, low-cost model such as qwen-flash in non-thinking mode.
import json
from openai import OpenAI
import os

# If the client variable isn't defined in the previous code block, uncomment the lines below
# client = OpenAI(
#   api_key=os.getenv("DASHSCOPE_API_KEY"),
#   base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
# )

try:
  json_object_from_thinking_model = json.loads(json_string)
  print("Generated standard JSON string")
except json.JSONDecodeError:
  print("Did not generate standard JSON string; fixing with a model that supports structured output")
  completion = client.chat.completions.create(
    model="qwen3.8-max",
    messages=[
      {
        "role": "system",
        "content": "You are a JSON format expert. Fix the user's JSON string to standard format",
      },
      {
        "role": "user",
        "content": json_string,
      },
    ],
    response_format={"type": "json_object"},
  )
  json_object_from_thinking_model = json.loads(completion.choices[0].message.content)

Error codes

If a call fails and returns an error message, see Error messages for resolution.