Skip to content

Structured outputs

Structured outputs make a model reply with JSON instead of prose — either any valid JSON object, or JSON that follows a schema you supply. It is the reliable way to extract fields, classify text, or feed a reply straight into code.

Use a model whose capabilities.json_mode is true in the model list.

JSON schema

const completion = await client.chat.completions.create({
  model: "gpt-5.5",
  messages: [
    { role: "system", content: "Extract the event details." },
    { role: "user", content: "Team offsite on March 3rd at the Lisbon office, 10am." },
  ],
  response_format: {
    type: "json_schema",
    json_schema: {
      name: "event",
      strict: true,
      schema: {
        type: "object",
        properties: {
          title: { type: "string" },
          date: { type: "string" },
          location: { type: "string" },
        },
        required: ["title", "date", "location"],
        additionalProperties: false,
      },
    },
  },
});

const event = JSON.parse(completion.choices[0].message.content);

With strict: true, models that support strict schemas only produce output that validates. Strict mode supports a subset of JSON Schema: set additionalProperties: false on every object and list every property in required (use a union with null for optional values).

JSON mode

For valid JSON without a fixed schema, use json_object. Say in the prompt that you want JSON and describe the shape, since the model has nothing else to go on:

{
  "model": "gemini-3.6-flash",
  "response_format": { "type": "json_object" },
  "messages": [
    { "role": "user", "content": "List three primary colours as JSON: {\"colours\": [...]}" }
  ]
}

On the Responses API

The schema goes under text.format:

{
  "model": "gpt-5.5",
  "input": "Team offsite on March 3rd at the Lisbon office, 10am.",
  "text": {
    "format": {
      "type": "json_schema",
      "name": "event",
      "strict": true,
      "schema": { "type": "object", "properties": { "title": { "type": "string" } }, "required": ["title"], "additionalProperties": false }
    }
  }
}

The SDK helpers for structured outputs (client.chat.completions.parse with Zod or Pydantic, client.responses.parse) send the same fields and work unchanged.

Things to check

  • A model that does not support a schema returns a 400 rather than quietly ignoring it. Pick another model or fall back to JSON mode.
  • A reply cut short by max_tokens (finish_reason: "length") is incomplete JSON. Leave room in the limit.
  • A model can still decline a request; check for a refusal before parsing.
  • Validate the parsed object anyway. A schema guarantees shape, not truth.
  • On the Messages endpoint, which has no response_format, the usual pattern is a single tool whose input_schema is your schema, with tool_choice forcing it.