Glossary/S/structured outputs
Model interfaces Also called: JSON mode, JSON schema output, response format, constrained decoding, grammar-constrained sampling

What are structured outputs?

Structured outputs is a provider feature that constrains a model's decoding so the response validates against a JSON Schema you supply. At each step the API restricts sampling to tokens that keep a partial parse legal, so required fields, enum choices and value types are guaranteed at generation time rather than checked afterwards. It is not the same as asking politely for JSON in the prompt, which the model can still break, and it is not a repair pass over free-form text.

What does the API actually do?

The provider compiles your JSON Schema into a grammar and uses it at sample time. At each decoding step the token distribution is masked so only tokens that continue a legal partial parse can be picked, which is called grammar-constrained sampling. When a required field would be missed, closing the object early is not a legal continuation; when an enum lists three allowed values, no fourth string can be produced. The reply is valid JSON that validates against the schema on the first try, so your code can call json.loads or an equivalent without a retry loop.

This is a decoding constraint, not a repair step. The model still chooses the content inside the schema, so a required customer_email field can come out empty, wrong or fabricated even though the shape is guaranteed. If accuracy matters you still need an eval on the values, not just a schema on the envelope.

How you turn it on: OpenAI, Anthropic, Gemini

OpenAI takes the schema on the request. On the Responses API you set text.format to {"type": "json_schema", "name": "...", "strict": true, "schema": {...}}; on Chat Completions the same object goes in response_format. With strict: true the schema is enforced by grammar-constrained sampling, every object must set additionalProperties: false, and every property must appear in required. The feature is supported from GPT-4o onwards.

Anthropic exposes two paths that share the same compiler. For a direct JSON reply you pass output_config.format with type: json_schema and a schema to the Messages API on Claude Opus 4 and newer, Sonnet 4 and newer, and Haiku 4.5. For a tool call whose arguments must match your schema, add strict: true to the tool definition alongside input_schema: the returned tool_use.input is then guaranteed to conform.

Gemini takes the schema on generationConfig. Set responseMimeType to application/json and pass responseSchema with the shape you want; the SDKs let you hand in a Pydantic model or a Zod schema and convert it for you. Gemini accepts a subset of JSON Schema (the OpenAPI 3.0 subset) rather than the fuller draft that OpenAI and Anthropic take.

Where the JSON Schema subset bites

Every provider accepts a subset of JSON Schema, and the parts they drop are usually the parts you reach for. Anthropic's structured outputs and strict tool use compile the same grammar and reject numeric constraints (minimum, maximum, multipleOf), string constraints (minLength, maxLength, pattern in most places), recursive schemas through $ref, and additionalProperties set to anything other than false. OpenAI's strict mode requires additionalProperties: false on every object and every property listed in required, and only a limited set of keywords is honoured. Gemini follows the OpenAPI 3.0 subset, so number and string constraints are limited and some combinators are not accepted.

The failure mode is a compile error at request time, not a bad response. Tighten the schema until the provider accepts it, then validate the numeric ranges, regex patterns and cross-field rules yourself in the calling code.

Structured outputs vs. tool calling vs. JSON mode

Three features get called "JSON mode" in casual writing and mean different things. Plain JSON mode, on OpenAI's older response_format: {"type": "json_object"} and similar options elsewhere, tells the model to return a JSON value but does not enforce a schema, so keys, types and required fields are still your problem. Structured outputs enforce a schema you supply and guarantee the shape at generation time. Tool calling asks the model to fill a schema for a named function; with strict mode on, the argument object is enforced the same way as a structured output.

Pick the tool-call form when the model's next action is to invoke a function and you want the arguments validated. Pick the direct structured-output form when you want a typed value back and no function to dispatch, such as extracting an invoice from an email or classifying a support ticket. Do not stack them: forcing a schema on top of a tool call that already has strict: true splits your validation across two places.

An OpenAI Responses request with a strict json_schema format

POST /v1/responses
{
  "model": "gpt-4o-2024-08-06",
  "input": [
    {"role": "user", "content": "Extract the invoice fields from this email: ..."}
  ],
  "text": {
    "format": {
      "type": "json_schema",
      "name": "invoice",
      "strict": true,
      "schema": {
        "type": "object",
        "properties": {
          "invoice_number": {"type": "string"},
          "total_cents":    {"type": "integer"},
          "currency":       {"type": "string", "enum": ["USD", "EUR", "GBP"]},
          "line_items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "sku":      {"type": "string"},
                "quantity": {"type": "integer"}
              },
              "required": ["sku", "quantity"],
              "additionalProperties": false
            }
          }
        },
        "required": ["invoice_number", "total_cents", "currency", "line_items"],
        "additionalProperties": false
      }
    }
  }
}

response.output_text:
{
  "invoice_number": "INV-2026-0918",
  "total_cents": 24990,
  "currency": "USD",
  "line_items": [
    {"sku": "A-100", "quantity": 2},
    {"sku": "B-220", "quantity": 1}
  ]
}

Every object sets additionalProperties: false and lists every property in required, which strict mode enforces. The reply parses on the first try, but the values (invoice number, totals, SKUs) are still model-generated and worth checking with an eval.

Common questions

Structured outputs: frequently asked

Do structured outputs stop the model from hallucinating?

No. The schema fixes the shape of the reply, not the truth of the values inside it. A required customer_email is guaranteed to be a string, but the model can still fabricate the address. Treat structured outputs as a parser guarantee and put an eval on the values that matter.

Is JSON mode the same as structured outputs?

No. JSON mode (OpenAI's older response_format: {"type": "json_object"} and similar options on other providers) tells the model to reply with a JSON value but does not enforce a schema, so keys and types are still your problem. Structured outputs bind the reply to a schema you supply and guarantee it at generation time.

Should I use structured outputs or a tool call?

Use a tool call when the reply is meant to invoke a function and the argument object is the payload; set strict: true on the tool so the arguments match the schema. Use direct structured outputs when the reply is data for your own code to consume, like an extraction or classification. Do not stack both on the same call.

Why does the API reject my schema?

Every provider accepts a subset of JSON Schema and enforces it at compile time. Common rejections are a missing additionalProperties: false, a property left out of required, numeric constraints like minimum or maximum, string constraints like pattern or minLength, and recursive $ref. Trim the schema to the accepted subset and validate the extra rules in your own code.

Sources

Where these facts come from