Skip to content
HUIAHUB

GuideModels & inference

LLM structured outputs with JSON schema in the OpenAI and Claude APIs

Get schema-valid JSON from OpenAI and Claude with LLM structured outputs, JSON schema and strict tool use, and know what each API guarantees.

Edited by Published 6 min read6 sources

Both the OpenAI and Claude APIs offer LLM structured outputs with JSON schema: you send a schema, and the model's response is constrained to match it. On OpenAI you set text.format to a json_schema with strict: true. On Claude you set output_config.format to a json_schema, and for tool calls you add strict: true to the tool definition.

Why asking for JSON in the prompt fails

A prompt that says "reply in JSON" is a request, and the model can ignore parts of it. Anthropic's structured outputs page lists what goes wrong without constrained decoding: invalid JSON syntax, missing required fields, inconsistent data types and schema violations that need retries.

OpenAI's JSON mode only partly fixes this. The OpenAI guide says JSON mode guarantees valid JSON but not adherence to any schema. JSON mode also needs the word "JSON" somewhere in the context, or the API returns an error. Without that instruction, the guide warns, the model may emit whitespace until it hits the token limit.

Structured outputs go further. Both providers compile your schema and restrict which tokens the model can sample, so the output parses and has the fields and types you declared.

OpenAI structured outputs with a JSON schema

OpenAI's Structured Outputs guide (opens in a new tab) says the feature is available on models starting with GPT-4o, and recommends gpt-6-astra for new projects. With response_format of type json_schema, the supported snapshots are gpt-4o-mini, gpt-4o-mini-2024-07-18, gpt-4o-2024-08-06 and later. The feature works in the Responses, Chat Completions, Assistants, Fine-tuning and Batch APIs.

Use the SDK helper with a Pydantic model

The Python SDK turns a Pydantic model into a schema and parses the reply:

from openai import OpenAI
from pydantic import BaseModel

client = OpenAI()

class CalendarEvent(BaseModel):
    name: str
    date: str
    participants: list[str]

response = client.responses.parse(
    model="gpt-6-astra",
    input=[
        {"role": "system", "content": "Extract the event information."},
        {"role": "user", "content": "Alice and Bob are going to a science fair on Friday."},
    ],
    text_format=CalendarEvent,
)

event = response.output_parsed

The JavaScript SDK does the same with a Zod schema and zodTextFormat() from openai/helpers/zod.

Send a raw JSON schema

Without a helper, the Responses API takes the schema under text.format:

response = client.responses.create(
    model="gpt-6-astra",
    input="Extract the event: Alice and Bob are going to a science fair on Friday.",
    text={
        "format": {
            "type": "json_schema",
            "name": "event",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"},
                    "date": {"type": "string"},
                    "participants": {"type": "array", "items": {"type": "string"}},
                },
                "required": ["name", "date", "participants"],
                "additionalProperties": False,
            },
        }
    },
)

OpenAI strict function calling

For tools, OpenAI applies the same engine through strict: true on a function definition. The function calling guide says strict mode needs additionalProperties: false on every object and every property listed in required. If you omit strict, Responses requests try to normalize the schema into strict mode and fall back to best-effort calling when they cannot. Chat Completions requests stay non-strict by default.

Claude structured outputs and strict tool use

Anthropic's structured outputs documentation (opens in a new tab) describes two features that can be used separately or in the same request:

  • JSON outputs, set with output_config.format, control the shape of Claude's response.
  • Strict tool use, set with strict: true on a tool, guarantees that tool names and inputs match the tool's schema.

Supported models listed on that page include claude-opus-5-5, claude-sonnet-5-5, claude-opus-4-6, claude-sonnet-4-5-20250929 and claude-haiku-4-5-20251001.

Claude JSON outputs with a Pydantic model

from anthropic import Anthropic
from pydantic import BaseModel

class ContactInfo(BaseModel):
    name: str
    email: str
    plan_interest: str
    demo_requested: bool

client = Anthropic()

response = client.messages.parse(
    model="claude-opus-5-5",
    max_tokens=1024,
    messages=[{
        "role": "user",
        "content": "Extract the key information from this email: John Smith "
                   "([email protected]) wants a demo of the Enterprise plan next Tuesday.",
    }],
    output_format=ContactInfo,
)

print(response.parsed_output)

The SDK sends the schema as output_config.format, validates the response and returns the parsed model in parsed_output. In TypeScript, zodOutputFormat() from @anthropic-ai/sdk/helpers/zod plays the same role. A raw schema goes in output_config: {"format": {"type": "json_schema", "schema": {...}}}.

Anthropic notes that the older output_format request parameter has moved to output_config.format. The raw parameter now needs the structured-outputs-2025-11-13 beta header, or the API returns a 400 error.

Strict tool use and forced tool calls

The strict tool use page (opens in a new tab) gives an example of what strict mode prevents: a booking tool expecting passengers: 2 receiving "two" or "2" instead.

A common older pattern was to define one tool and force Claude to call it with tool_choice, then read the tool input as your JSON. Check your model before relying on that.

Anthropic's tool definition docs (opens in a new tab) say Claude Opus 5.5, Claude Sonnet 5.5, Claude Fable 5.1 and Claude Mythos 5.1 return a 400 error for tool_choice of type any or tool. For those models, Anthropic points to auto with strict tool use, or to JSON outputs when you need a fixed response shape. Manual extended thinking also blocks forced tool use.

Which JSON schema features each API supports

The two providers accept different subsets of JSON Schema. Sending an unsupported keyword with strict mode on produces an error in both APIs, so check this before you port a schema.

Schema featureOpenAI Structured OutputsClaude structured outputs
Optional fieldsEvery field must be in required; emulate optional with a null union typeAllowed; at most 24 optional parameters across all strict schemas in a request
additionalPropertiesMust be falseMust be false
Root objectMust be an object, not anyOfNot stated
anyOfSupportedSupported, with limits
allOfNot supportedSupported, except with $ref
$ref and definitionsSupportedSupported; external $ref not supported
Recursive schemasSupportedNot supported
minimum, maximum, multipleOfSupported (not for fine-tuned models)Not supported; SDK helpers move them into descriptions
minLength, maxLengthNot in the supported keyword listNot supported
minItems, maxItemsSupportedminItems of 0 or 1 only
patternSupportedSupported, without backreferences, lookarounds or \b
String formatsdate-time, time, date, duration, email, hostname, ipv4, ipv6, uuidSame list plus uri
Size limits5,000 properties, 10 nesting levels, 1,000 enum values, 120,000 characters of names and values20 strict tools, 24 optional parameters, 16 union-type parameters, 180-second compile timeout
Key orderSame as schemaSchema order, but required properties come before optional ones

Both providers also add latency the first time they see a schema. OpenAI says later requests with the same schema avoid it. Anthropic caches compiled grammars for 24 hours from last use. Changing only name or description fields does not reset Anthropic's cache.

Validate structured output anyway

A schema guarantee covers structure. It does not cover whether the values are right, and both providers document cases where the output does not match the schema at all.

Check these before you trust a parsed object:

  1. Refusals. OpenAI returns a refusal content item instead of your schema. Claude sets stop_reason: "refusal" with a 200 status, and the output may not match the schema.
  2. Truncation. An OpenAI response with status: "incomplete" and reason max_output_tokens, or a Claude response with stop_reason: "max_tokens", can hold partial JSON.
  3. Enum casing on Claude. Anthropic says Claude may return an enum or const value that differs only in capitalization, with no error. Compare enum values case-insensitively.
  4. Constraints the API dropped. Claude's SDK helpers strip unsupported constraints such as minimum before sending, then check them on the response. A raw schema skips that check.
  5. Wrong but valid values. OpenAI's guide says Structured Outputs can still contain mistakes, and that unrelated input can lead the model to invent values to fill the schema.

Running the response through your own model class costs one line in Python:

event = CalendarEvent.model_validate_json(raw_text)

OpenAI's guide also recommends generating schemas from your types with SDK helpers, so the schema and your code do not drift apart.

Decision table: LLM structured outputs with JSON schema

Use this as a starting point. The rows reflect the documented behavior of each API as of October 2026.

You needOpenAIClaude
The final answer as JSON matching a schematext.format with type: "json_schema", strict: trueoutput_config.format with type: "json_schema"
Arguments for your own functions, always typed correctlyFunction calling with strict: trueTool definition with strict: true
Both typed tool calls and a JSON final answerStrict functions plus text.formatStrict tools plus output_config.format in one request
Valid JSON on an older model without schema supportJSON mode (type: "json_object") plus your own validationNot documented as a separate mode
Optional fields without null placeholdersNot available; use null unionsSupported, within the 24-parameter limit
Recursive data such as treesSupportedNot supported; flatten the structure
Numeric ranges or string length enforcedNumeric ranges supported; length limits not listed; validate in codeValidate in code, or let the SDK helper validate
Citations in the same responseNot covered in the OpenAI guideNot available: citations with output_config.format return a 400 error

Frequently asked questions

Do structured outputs cost more tokens?

On Claude, yes, slightly: Anthropic adds a system prompt that explains the expected format, and those tokens are billed like any other input. Changing `output_config.format` also invalidates any prompt cache for that conversation. The OpenAI guide reviewed here does not describe an extra prompt.

Can I stream a structured response?

Both providers support it. OpenAI's guide shows streaming responses and function call arguments while they are generated, and Anthropic lists streaming as compatible with structured outputs. Partial chunks will not parse as complete JSON until the stream ends, so validate after the final chunk.

Does using structured outputs affect data retention on Claude?

Anthropic says prompts and responses are processed with zero data retention, but the schema itself is cached for up to 24 hours after last use. For HIPAA workloads, Anthropic says protected health information must not appear in schema property names, enum values, const values or patterns.

Should a schema field ask the model to explain its reasoning?

Anthropic warns that a field asking for the model's thinking or step-by-step reasoning may trigger a `reasoning_extraction` refusal on Claude. Ask for a short explanation or supporting evidence instead.

Can I use structured outputs with message prefilling on Claude?

No. Anthropic lists message prefilling as incompatible with JSON outputs. Put formatting guidance in the schema and its field descriptions instead.

Sources

  1. 1.Structured model outputs | OpenAI APIOfficial docsPrimarydevelopers.openai.com
  2. 2.Structured outputs - Claude Platform DocsOfficial docsPrimaryplatform.claude.com
  3. 3.Tool use with Claude - Claude Platform DocsOfficial docsPrimaryplatform.claude.com
  4. 4.developers.openai.com/api/docs/guides/function-callingOfficial docsPrimary
  5. 5.platform.claude.com/docs/en/agents-and-tools/tool-use/strict-tool-useOfficial docsPrimary
  6. 6.platform.claude.com/docs/en/agents-and-tools/tool-use/define-toolsOfficial docsPrimary

Spotted an error? See our corrections policy.

Enjoyed this? Get the weekly brief.

New guides, Project & Tool of the Week and the AI developments that matter. One email, no spam.

Read next