Skip to the page
Tecyfy

Structured outputs vs JSON mode: one schema on OpenAI, Claude and Gemini

JSON mode promises JSON that parses; structured outputs promise JSON that fits your schema. OpenAI, Claude and Gemini all offer the second now, but each accepts a different part of JSON Schema: optional fields, number limits, string lengths and recursion work on one and fail, or are silently dropped, on another. The same extraction written for all 3, and a schema that works on every one.

Tecyfy · Published · 8 min read

Red liquid poured into a mould of neat compartments above the word JSON, with a spill beside it

JSON mode promises JSON that parses; structured outputs promise JSON that fits your schema. With JSON mode, a field can be missing, renamed or the wrong type, and your code finds out when it crashes. With structured outputs, the API turns your schema into a grammar and the model can only write answers that match it. Use structured outputs whenever the model supports them.

What the short answer leaves out: OpenAI, Claude and Gemini all offer structured outputs now, but each accepts a different part of JSON Schema. A schema that works on one can return an error on the next, or be accepted with some of its rules quietly dropped. The idea is explained in Structured OutputStructured OutputA setting that makes a model's answer follow a JSON schema you give it, so your code can read fields such as a category or an order number without parsing free text.; this article is about moving a schema between makers.

The same extraction on all 3

One schema: pull a support ticket's category, order number and urgency out of an email.

schema = {
    "type": "object",
    "properties": {
        "category": {"type": "string", "enum": ["delivery", "refund", "account", "other"]},
        "order_id": {"type": ["string", "null"]},
        "urgent": {"type": "boolean"},
    },
    "required": ["category", "order_id", "urgent"],
    "additionalProperties": False,
}
email = "Where is my parcel? Order 88231. I need it before Friday."

OpenAI (Responses API): the schema goes in text.format with strict: True.

from openai import OpenAI

r = OpenAI().responses.create(
    model="gpt-6.1-sol",
    input=email,
    text={"format": {"type": "json_schema", "name": "ticket", "strict": True, "schema": schema}},
)
print(r.output_text)

Claude (Messages API): the schema goes in output_config.format.

import anthropic

r = anthropic.Anthropic().messages.create(
    model="claude-sonnet-5-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": email}],
    output_config={"format": {"type": "json_schema", "schema": schema}},
)
print(next(b.text for b in r.content if b.type == "text"))

Gemini (Google GenAI SDK): the schema goes in response_format with the JSON media type.

from google import genai

r = genai.Client().interactions.create(
    model="gemini-3.8-flash",
    input=email,
    response_format={"type": "text", "mime_type": "application/json", "schema": schema},
)
print(r.output_text)

Each returns an object with exactly those 3 fields, such as {"category": "delivery", "order_id": "88231", "urgent": true}. This schema is deliberately plain: every field required, null allowed where the value may be absent, a string enum, no limits. That is the part all 3 accept.

Where they differ

Schema featureOpenAIClaudeGemini
Optional fields (not in required)Not allowed: list every field, use nullAllowed, up to 24 per requestAllowed
additionalPropertiesMust be falseMust be falsetrue, false or a schema
minimum, maximumEnforced400 error (SDK helpers move it to the description)Enforced
minLength, maxLengthNot documented (listed as unsupported only for fine-tuned models)400 errorSilently dropped
minItems, maxItemsEnforcedOnly minItems of 0 or 1Enforced
pattern (regex)SupportedSimple patterns onlyNot listed
Recursive schemasSupportedNot supportedSupported
Output key orderSchema orderRequired fields first, then optionalNot stated
An unsupported keywordError400 errorSome are dropped with no error
Try itWill this schema work on every maker?
My schema uses
FeatureOpenAIClaudeGemini
An optional fielderrorenforcedenforced
  • OpenAI needs every field in required; use a null type instead.

Not portable

OpenAI: refused; Claude: works; Gemini: works.

From each maker's structured output docs. Claude's SDK helpers can rewrite some refused keywords into descriptions and check them after the answer arrives. Whatever the maker, validate limits in your own code after parsing.

3 rows cause most of the surprises:

  • Optional fields. OpenAI rejects a strict schema whose properties are not all listed in required. A Pydantic field with a default of None is left out of required, so a schema generated from it fails. Make the field required with type ["integer", "null"] instead; the model then writes null when there is nothing to say.
  • Limits. minimum: 1 on a quantity is enforced by OpenAI and Gemini and refused by Claude's API. Claude's SDK helpers strip it from the schema they send, add it to the field's description and check it on the answer, so the limit is still checked, after the model writes. Gemini drops minLength and maxLength without an error, so a length rule you think is enforced may not be.
  • Recursion. A tree (categories with sub-categories, a comment thread) works on OpenAI and Gemini and returns an error on Claude. Flatten it into a list with parent ids.

JSON mode, where it still exists

OpenAI still offers JSON mode with text.format set to {"type": "json_object"}. 2 traps come with it: the word "JSON" must appear somewhere in your messages or the API returns an error, and without a clear instruction the model can write whitespace until it hits the token limit. It also makes no promise about your keys. It is for older models that lack structured outputs; on current models there is no reason to choose it.

What none of them guarantee

  • Right answers. The schema forces a category field, not the correct category. Test with an eval set.
  • Refusals and cut-offs. A refused request, or an answer that hits the output token limit, can stop mid-object. Check the stop reason before you parse.
  • Exact enum spelling on Claude. Claude's docs warn that an enum value may come back differing only in capital letters. Compare enum values without regard to case.
  • Rules the grammar drops. Validate lengths, counts and any limit your target maker does not enforce in your own code after parsing, with Pydantic or Zod.

Tools or a schema?

Structured outputs shape the final answer. Tools, or function calling, let the model ask your code to do something before it answers. If you need both (look up the order, then return the ticket object), use both in one request: strict tools while the model works, a schema for the answer. The difference is covered in MCP vs function calling for the agent side.

What to do

  1. Use structured outputs, not JSON mode, on every current model.
  2. If you might switch makers, keep schemas to the common part: all fields required, null for absent values, string enums, no limits, no recursion.
  3. Validate limits and lengths yourself after parsing, on every maker.
  4. Check for refusals and cut-off answers before reading fields.

Our free AI engineering course builds an app that reads model output with a schema, and the prices for each model named here are on /prices.

Questions

What is the difference between structured outputs and JSON mode?
JSON mode makes the model return JSON that parses, but with no promise about its keys or types. Structured outputs take a JSON schema and constrain the model's answer to fit it, so required fields are present and types and enum values match. OpenAI recommends structured outputs over JSON mode wherever they are supported.
Does Claude have JSON mode?
Claude's API offers structured outputs: pass a JSON schema in output_config.format, or set strict true on a tool. The answer is constrained to the schema through constrained sampling, with refusals and answers cut off at max_tokens as the documented exceptions.
How do I get JSON output from Gemini?
Set response_format with type text, mime_type application/json and your JSON schema in the schema field. The Google GenAI SDK accepts a Pydantic or Zod model. Gemini enforces a subset of JSON Schema and silently strips some keywords it cannot enforce, such as minLength and maxLength.
Can I use the same JSON schema on OpenAI, Claude and Gemini?
Yes, if you keep to the part all 3 accept: objects with additionalProperties false, every field required (use a null type for a field that may be empty), enums of strings, and no number limits, length limits or recursion. Check limits such as minimum or maxLength in your own code after parsing.
Do structured outputs guarantee correct answers?
No. They guarantee the shape, not the content. A schema can force a category field but not make the category right. Refusals and answers cut off by the output token limit can also break the shape, and Claude may return an enum value that differs only in capital letters.