Back to Blog
Python

Python OpenAI Structured Outputs with JSON Schema

Learn how to use OpenAI structured outputs in Python to enforce a JSON schema on model responses, parse the returned JSON, and validate it with Pydantic.

OpenAI APIJSON SchemaStructured OutputsPythonLLM
Illustration of a Python code block transforming into a structured JSON object with a schema diagram, symbolizing OpenAI structured outputs.

When you call the OpenAI API and ask for JSON, the model may return valid JSON that does not match the shape your application expects. Field names drift, types change, or extra keys appear. Structured outputs solve this by making the model follow a schema you define, so the response is more predictable and directly usable in your code.

What Structured Outputs Solve

Without a schema, a prompt like "return a JSON object with a name and age" can produce {"name": "Alice", "age": 30} one time and {"person": {"name": "Alice", "age": 30}} the next. Your parsing code then has to handle multiple possible shapes, and a single unexpected key can break the pipeline.

Structured outputs let you declare the JSON structure the model should produce. The API uses your JSON schema to constrain the generation process, so the returned object matches the schema more consistently than free-form JSON. This is especially useful when the output feeds directly into a database, an API response, or a data-processing function that expects a fixed structure.

For the Chat Completions API, you provide a response_format parameter that specifies type: "json_schema" and the schema itself. The model is then constrained to generate JSON that conforms to that schema, including required fields and type constraints.

Defining a JSON Schema for the Model

A JSON schema describes the expected structure using standard JSON Schema syntax. For structured outputs, the schema must be an object type. You define properties, their types, and which ones are required.

Here is the json_schema object you would pass inside response_format:

{ "name": "person_extraction", "strict": true, "schema": { "type": "object", "properties": { "name": { "type": "string" }, "age": { "type": "integer" } }, "required": ["name", "age"], "additionalProperties": false } }

The strict flag tells the API to enforce the schema strictly. When strict mode is on, the model is constrained to produce only the listed properties, and required fields must be present. This is the behavior you want for most production use cases.

The schema itself is expressed as a JSON Schema object, subject to the API’s supported subset. You can use a tool that generates JSON Schema, or write it by hand. For complex nested structures, consider using a library like Pydantic to generate the schema from a Python model, then pass that schema to the API.

Passing the Schema to the OpenAI API

In the OpenAI Python SDK, you pass the schema through the response_format parameter of the chat completion call. The exact parameter shape may vary slightly depending on the SDK version, but the general pattern is consistent.

from openai import OpenAI client = OpenAI() response = client.chat.completions.create( model="gpt-4o", messages=[ { "role": "user", "content": "Extract the name and age from: 'Alice is 30 years old.'" } ], response_format={ "type": "json_schema", "json_schema": { "name": "person_extraction", "strict": True, "schema": { "type": "object", "properties": { "name": {"type": "string"}, "age": {"type": "integer"} }, "required": ["name", "age"], "additionalProperties": False } } } ) content = response.choices[0].message.content print(content)

When you run this, the content field contains a JSON string that should match the schema. In this example, it would be something like {"name": "Alice", "age": 30}.

Notice that the schema is nested inside json_schema. The name field is required and should be a descriptive identifier. Set strict to True to enforce the constraints.

Handling the Response and Validating Output

Even with structured outputs, the API returns the JSON as a string in the content field. You still need to parse it with json.loads(). The advantage is that the structure is predictable, so you can deserialize it into a Python object or a Pydantic model. Keep validation as a safety net because the model is not infallible.

import json content = response.choices[0].message.content person = json.loads(content) print(person["name"], person["age"])

With Pydantic v2, you can define a model that matches the schema and validate the parsed data directly:

from pydantic import BaseModel, ValidationError class Person(BaseModel): name: str age: int try: person = Person.model_validate_json(content) print(person) except ValidationError as e: print("Validation failed:", e)

This gives you a second layer of safety. Even if the model occasionally violates the schema, the validation step catches it before the data reaches your business logic.

Strict Mode and Schema Constraints

Strict mode is the key to reliable structured outputs. When strict: true is set, the API applies the schema constraints during generation:

  • Required fields must be present.
  • Additional properties are not allowed.
  • Property types must match the schema.
  • The output is constrained to be valid JSON.

This addresses the most common failure modes: missing keys, extra keys, and type mismatches. However, strict mode uses a subset of JSON Schema and has some limitations. The schema must be an object, and not every JSON Schema keyword is available. Check the current OpenAI documentation for the exact list of supported keywords and for how to represent nullable fields, because the syntax can differ from a generic nullable flag.

When you define the schema, keep it as simple as possible. Complex nested structures are allowed, but each level must follow the same rules. If you need an array of objects, define the item schema explicitly.

Practical Considerations: Cost, Latency, and Compatibility

Structured outputs do not change the number of tokens in the generated content, but constrained decoding may add a small amount of latency. In practice, the difference is small for typical schemas.

Compatibility depends on the model and the API version. Not all models support structured outputs with a JSON schema. Check the OpenAI documentation for the model you are using; some models may only support json_object mode without a custom schema. The Python SDK version also matters; ensure you have a recent version that includes the json_schema response format.

Another operational concern is error handling. If the model fails to produce a response that conforms to the schema, the API may return an error. In that case, you should catch the exception and retry with a different prompt or fall back to a manual repair step. Do not assume that structured outputs are infallible; they reduce the probability of malformed output but do not eliminate it entirely.

Common Failure Patterns and How to Avoid Them

One common mistake is forgetting to set additionalProperties: false on every object schema when using strict mode. Without it, the schema is not fully constrained, and the API may not enforce the exact shape you want. Always set it explicitly when you need a fixed structure.

Another issue is using a schema that is too restrictive. For example, if you require a field that the model cannot infer from the input, it may guess a value. Make sure the schema matches the information available in the prompt. If a field is genuinely optional, handle its absence in your parsing code rather than assuming every response will include it.

A third problem is mixing up the response_format syntax. The json_schema object should contain name, strict, and schema fields. If you omit strict, the API may still return JSON but with less enforcement. Keep the fields explicit.

Finally, be careful with nested schemas. Each nested object must also have additionalProperties: false if you want strict behavior at all levels. The API does not automatically apply that setting to nested objects; you must specify it in each object definition.

By following these patterns, you can rely on structured outputs to deliver consistent, schema-valid JSON from the OpenAI API, making your Python integration more robust and maintainable.

OpenAI Structured Outputs with JSON Schema in Python | RYUSLOG DEV