Structured Outputs

JSON Mode

Get guaranteed JSON responses that match your schema. Perfect for data extraction, API responses, and type-safe applications.

Overview

Structured outputs ensure the model returns valid JSON matching your specification. Two modes are available:

JSON Mode

Guarantees valid JSON output. The structure is flexible—you describe the format in your prompt.

JSON Schema

Guarantees the output matches your exact JSON Schema definition with strict: true.

Supported Models

ModelJSON ModeJSON SchemaStrict Mode
gpt-4o
gpt-4o-mini
claude-3.5-sonnetVia prompt
gemini-2.0-flash

JSON Mode

The simplest way to get JSON responses. Set response_format: { type: "json_object" } and include "JSON" in your prompt:

TypeScript
const response = await client.chat.completions.create({
  model: 'gpt-4o',
  response_format: { type: 'json_object' },
  messages: [
    {
      role: 'system',
      content: 'You are a helpful assistant. Always respond with valid JSON.'
    },
    {
      role: 'user',
      content: 'List 3 programming languages with their year of creation.'
    }
  ]
});

const data = JSON.parse(response.choices[0].message.content);
console.log(data);
// {
//   "languages": [
//     { "name": "Python", "year": 1991 },
//     { "name": "JavaScript", "year": 1995 },
//     { "name": "Go", "year": 2009 }
//   ]
// }

Important: When using JSON mode, you MUST include the word "JSON" in your system or user message.

JSON Schema Mode

Define an exact schema and get guaranteed conformance with strict mode:

TypeScript
const response = await client.chat.completions.create({
  model: 'gpt-4o',
  response_format: {
    type: 'json_schema',
    json_schema: {
      name: 'user_profile',
      strict: true,
      schema: {
        type: 'object',
        properties: {
          name: { type: 'string' },
          age: { type: 'integer' },
          email: { type: 'string', format: 'email' },
          interests: {
            type: 'array',
            items: { type: 'string' }
          }
        },
        required: ['name', 'age', 'email'],
        additionalProperties: false
      }
    }
  },
  messages: [
    {
      role: 'user',
      content: 'Create a profile for a 28-year-old software engineer named Alex who likes hiking.'
    }
  ]
});

const profile = JSON.parse(response.choices[0].message.content);
// Guaranteed to match the schema:
// {
//   "name": "Alex",
//   "age": 28,
//   "email": "alex@example.com",
//   "interests": ["hiking", "software engineering"]
// }

Using with Zod

Combine Zod schemas with structured outputs for end-to-end type safety:

TypeScript
import { z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';

// Define your schema with Zod
const ProductSchema = z.object({
  name: z.string().describe('Product name'),
  price: z.number().positive().describe('Price in USD'),
  category: z.enum(['electronics', 'clothing', 'food', 'other']),
  inStock: z.boolean(),
  tags: z.array(z.string()).optional()
});

// Convert to JSON Schema
const jsonSchema = zodToJsonSchema(ProductSchema, 'Product');

const response = await client.chat.completions.create({
  model: 'gpt-4o',
  response_format: {
    type: 'json_schema',
    json_schema: {
      name: 'product',
      strict: true,
      schema: jsonSchema.definitions?.Product || jsonSchema
    }
  },
  messages: [
    {
      role: 'user',
      content: 'Create a product listing for wireless earbuds priced at $79.'
    }
  ]
});

// Parse and validate with Zod
const product = ProductSchema.parse(JSON.parse(response.choices[0].message.content));
console.log(product);
// TypeScript knows the exact type!

Install with: npm install zod zod-to-json-schema

Data Extraction Example

Extract structured data from unstructured text like invoices, receipts, or documents:

TypeScript
const response = await client.chat.completions.create({
  model: 'gpt-4o',
  response_format: {
    type: 'json_schema',
    json_schema: {
      name: 'invoice_data',
      strict: true,
      schema: {
        type: 'object',
        properties: {
          vendor_name: { type: 'string' },
          invoice_number: { type: 'string' },
          date: { type: 'string', format: 'date' },
          line_items: {
            type: 'array',
            items: {
              type: 'object',
              properties: {
                description: { type: 'string' },
                quantity: { type: 'integer' },
                unit_price: { type: 'number' },
                total: { type: 'number' }
              },
              required: ['description', 'quantity', 'unit_price', 'total']
            }
          },
          subtotal: { type: 'number' },
          tax: { type: 'number' },
          total: { type: 'number' }
        },
        required: ['vendor_name', 'invoice_number', 'date', 'line_items', 'total']
      }
    }
  },
  messages: [
    {
      role: 'user',
      content: `Extract structured data from this invoice:
      
      ACME Corp Invoice #INV-2024-001
      Date: January 15, 2024
      
      Widget Pro x5 @ $29.99 = $149.95
      Gadget Basic x2 @ $19.99 = $39.98
      
      Subtotal: $189.93
      Tax (10%): $18.99
      Total: $208.92`
    }
  ]
});

Enum Constraints

Use enums to constrain values to a specific set:

TypeScript
const response = await client.chat.completions.create({
  model: 'gpt-4o',
  response_format: {
    type: 'json_schema',
    json_schema: {
      name: 'sentiment_analysis',
      strict: true,
      schema: {
        type: 'object',
        properties: {
          sentiment: {
            type: 'string',
            enum: ['positive', 'negative', 'neutral', 'mixed']
          },
          confidence: {
            type: 'number',
            minimum: 0,
            maximum: 1
          },
          keywords: {
            type: 'array',
            items: { type: 'string' },
            maxItems: 5
          }
        },
        required: ['sentiment', 'confidence', 'keywords']
      }
    }
  },
  messages: [
    {
      role: 'user',
      content: 'Analyze: "The product quality is great but shipping was slow."'
    }
  ]
});

// Output is guaranteed to have valid enum values:
// { "sentiment": "mixed", "confidence": 0.85, "keywords": ["quality", "great", "shipping", "slow"] }

Schema Limitations

Supported Types

string, number, integer, boolean, array, object, null

Strict Mode Requirements

  • • All properties must be in required array
  • • Must set additionalProperties: false
  • • Max 100 properties per object
  • • Max 5 levels of nesting

Not Supported in Strict Mode

  • oneOf, anyOf, allOf
  • $ref references
  • pattern for strings
  • minItems, maxItems for arrays

Best Practices

Keep Schemas Simple

Simpler schemas produce more reliable outputs. Split complex extractions into multiple calls if needed.

Add Descriptions

Use the description field in your schema to guide the model on expected values.

Handle Optional Fields

In strict mode, use type: ["string", "null"] for optional fields instead of omitting them.

Validate on Your End

Even with strict mode, validate the response with Zod or similar for defense in depth.

Related Guides