Typed responses with json_schema
Pass a JSON schema in response_format and the model is constrained to return JSON that matches. Strict mode rejects unknown fields; in non-strict mode the model still tries to match but can drift on edge cases. Support is model-dependent: only models tagged json_schema enforce a schema on a plain reply — see Model support below.
Python
import os, json
from openai import OpenAI
client = OpenAI(
base_url="https://api.quicksilverpro.io/v1",
api_key=os.environ["QSP_KEY"],
)
resp = client.chat.completions.create(
model="deepseek-v4-flash",
messages=[{
"role": "user",
"content": "Extract the city and temperature from: It was 18C in Paris today.",
}],
response_format={
"type": "json_schema",
"json_schema": {
"name": "weather_reading",
"strict": True,
"schema": {
"type": "object",
"properties": {
"city": {"type": "string"},
"temp_c": {"type": "number"},
},
"required": ["city", "temp_c"],
"additionalProperties": False,
},
},
},
)
data = json.loads(resp.choices[0].message.content)
print(data["city"], data["temp_c"]) # Paris 18Strict vs non-strict
- Strict (
strict: true): output is constrained at decode time to match the schema. Unknown keys are impossible.additionalProperties: falseis required on every object in the schema. Recommended for production. - Non-strict (
json_object): output is JSON but not schema-constrained. The model tries to match but can drift on long-tail prompts. Use only for prototyping.
Schema rules
Strict mode has a few constraints to be aware of:
- Every object must have
additionalProperties: falseandrequiredlisting every property. enum,const,oneOfare supported. Recursive schemas via$refare also supported.- Defaults aren't enforced — if you need a default, post-process the parsed JSON in your client.
Model support
Only models listing JSON Schema under Capabilities on their model page enforce response_format. Most of the catalog does. The ones that do not are qwen3.8-27b, qwen3.8-flash-next, glm-5.3, glm-5.3-prime, glm-5.3-flash, gpt-oss-120b, claude-opus-5-5, claude-opus-5, claude-fable-5-1, claude-fable-5, claude-opus-4-8, claude-sonnet-5, claude-haiku-4-5, gemini-3-pro-image. Those models accept the request and answer in prose, so treat a schema as advisory there rather than enforced.
To get schema-shaped output from a Claude model, declare the schema as a tool with strict: true and read the arguments off the tool call. The arguments are then constrained to your schema — but only when the model decides to call the tool. On the Claude models that accept a forced choice you can pin that down with tool_choice; on claude-opus-5-5, which chooses for itself, the reply may arrive as plain text instead, so handle a missing tool call or pick a model that supports forcing. See Tool calling. The per-model capability list on Models explains the Claude behaviour, and each model page lists that model's capabilities.
When to use
- Structured extraction — pulling typed fields from free-form text.
- Form-filling agents — guaranteeing the agent emits keys your downstream code can consume.
- Pipelines— when the model's output is the input to the next stage, schema-constrained output eliminates a class of parse errors.
For function/tool calling, the same constraint mechanism is available via the tools[].function.parameters field. See Tool calling.