OpenAI Structured Outputs (JSON Schema) and JSON mode
Status: DOCUMENTED + LIVE_VERIFIED (Responses text.format json_schema strict and Chat response_format json_schema strict, 2026-09-18; JSON-mode guard error verified). Machine-readable: parameter rows text.format(*) in generated/fragments/parameters/openai-responses.json and response_format(*) in openai-chat-completions.json.
Sources
- https://developers.openai.com/api/docs/guides/structured-outputs (supported models, schema subset, streaming, JSON mode, edge cases)
- https://developers.openai.com/api/docs/guides/migrate-to-responses#6-update-structured-outputs-definitions
- OpenAPI
TextResponseFormatConfiguration,TextResponseFormatJsonSchema,ResponseFormatJsonSchema,ResponseFormatJsonObject,ResponseFormatText
Last verified: 2026-09-18
1. Two shapes for the same feature
| Responses API | Chat Completions API | |
|---|---|---|
| Plain text (default) | "text": {"format": {"type": "text"}} |
"response_format": {"type": "text"} |
| Structured Outputs | "text": {"format": {"type": "json_schema", "name": "…", "schema": {...}, "strict": true, "description": "…"}} — flat |
"response_format": {"type": "json_schema", "json_schema": {"name": "…", "schema": {...}, "strict": true, "description": "…"}} — nested wrapper |
| JSON mode (legacy) | "text": {"format": {"type": "json_object"}} |
"response_format": {"type": "json_object"} |
| Also available via | function tools with strict: true (Responses omits strict → tries strict, falls back to non-strict and returns strict:false) |
function tools with strict: true (non-strict by default) |
| Output location | output[].content[].text (SDK output_text, parse() → output_parsed) |
choices[].message.content (SDK parse() → message.parsed) |
| Refusals | output[].content[] part {type:"refusal", refusal} |
choices[].message.refusal |
name (required): a-z A-Z 0-9 _ -, ≤64 chars. strict defaults to false in the spec (docs recommend true). schema is an arbitrary JSON Schema object (additionalProperties: true in the spec).
2. Supported schema subset (strict mode)
- Types: string, number, integer, boolean, object, array, enum,
anyOf;$defs/$refand recursion ("$ref": "#") supported. - Constraints: string
pattern,format(date-time, time, date, duration, email, hostname, ipv4, ipv6, uuid); numbermultipleOf,minimum,maximum,exclusiveMinimum,exclusiveMaximum; arrayminItems,maxItems. (Not for fine-tuned models.) - Rules: root must be an object (no root
anyOf); all propertiesrequired(emulate optional with["string","null"]); every object needsadditionalProperties: false; noallOf,not,dependentRequired,dependentSchemas,if/then/else. - Limits: ≤5,000 properties, ≤10 nesting levels, ≤120,000 chars of property/definition/enum/const strings, ≤1,000 enum values (≤15,000 chars of enum strings when >250 values).
- Output keys follow schema key order. Unsupported schema +
strict:true→ 400. - Models: gpt-4o-mini / gpt-4o-2024-08-06 and later (all GPT-4.1, GPT-5.x, o-series). JSON mode also on gpt-3.5-turbo / gpt-4-*.
3. Edge cases to handle
status:"incomplete"/finish_reason:"length"→ JSON may be truncated; do not parse.- Refusal content part /
message.refusal→ no JSON. content_filter→ partial output.- JSON mode only guarantees syntactically valid JSON, not schema adherence; the request must mention "JSON" — live p3:
text.format.type=json_objectwith input "Reply with OK." → HTTP 400Response input messages must contain the word 'json' in some form to use 'text.format' of type 'json_object'(param:"input"). - Streaming: Responses emits
response.output_text.deltafragments of the JSON (SDKstream()yieldsresponse.output_text.deltawith accumulatedsnapshot); Chat emitsdelta.contentfragments — parse only when done. - Changing
text.format/schema invalidates the prompt-cache prefix (prompt_cache_diagnostics.reason:"text_format_changed"on GPT-5.6+).
4. Live excerpts (2026-09-18)
Responses (e), gpt-5.4-nano, max_output_tokens 32:
request {"text":{"format":{"type":"json_schema","name":"ok_reply","strict":true,"schema":{"type":"object","properties":{"answer":{"type":"string"}},"required":["answer"],"additionalProperties":false}}}}
response "text":{"format":{"type":"json_schema","description":null,"name":"ok_reply","schema":{…},"strict":true},"verbosity":"medium"}
output_text = "{\"answer\":\"OK.\"}" (usage 15 in / 22 out incl. formatting tokens)Chat (l3), gpt-4.1-nano: response_format.json_schema strict → content = "{\"answer\":\"OK\"}".
SDK helpers verified in examples: Python client.responses.parse(text_format=Model) → output_parsed; client.chat.completions.parse(response_format=Model) → message.parsed (see examples/openai/responses/structured_output.py, examples/openai/chat/structured_output.py).
5. Migration note
response_format is rejected on /v1/responses (use text.format); text is not a Chat parameter. Flatten json_schema.{name,schema,strict,description} into format when moving to Responses.