Skip to content
Last updated

Structured Outputs ensure model responses conform to predefined formats, enabling type-safe, predictable integration with your applications.

What are Structured Outputs?

Instead of parsing free-form text, Structured Outputs guarantee the model returns data in a specific format you define—whether simple JSON or complex schemas with nested objects and arrays.

Standard Output:               Structured Output:
-------------------           ---------------------
"The user is 25              {
years old and                  "name": "Alice",
lives in Paris."               "age": 25,
                               "location": "Paris"
                            }

Quick Start

Get started with JSON mode in 3 simple steps:

import requests
import os

# 1. Set output_mode to "json"
response = requests.post(
    "https://api.aitronos.com/v1/model/response",
    headers={"Authorization": f"Bearer {os.environ['FREDDY_API_KEY']}"},
    json={
        "organization_id": "org_abc123",
        "model": "ftg-3.0",
        "output_mode": "json",  # Enable JSON mode
        "inputs": [{
            "role": "user",
            "texts": [{
                "text": "Extract the product name, price, and rating from: 'iPhone 15 Pro - $999 - 4.5 stars'"
            }]
        }]
    }
)

# 2. Get guaranteed valid JSON
result = response.json()
data = result["output"][0]["content"][0]["text"]

# 3. Parse and use
import json
product = json.loads(data)
print(f"Product: {product['name']}, Price: {product['price']}")

That's it! The model returns valid JSON every time.

Output Modes

1. text Mode (Default)

Standard free-form text responses.

{
  "outputMode": "text",
  "inputs": [
    {"role": "user", "texts": [{"text": "What is Python?"}]}
  ]
}

Response:

Python is a high-level, interpreted programming language...

Use when:

  • Natural conversation
  • Creative writing
  • Open-ended queries

2. json Mode

Forces the model to return valid JSON. The model determines the structure based on your prompt.

{
  "organization_id": "org_abc123",
  "model": "ftg-3.0",
  "output_mode": "json",
  "inputs": [
    {
      "role": "user",
      "texts": [{"text": "Extract name, age, and city from: 'Alice is 25 and lives in Paris'"}]
    }
  ]
}

Response:

{
  "name": "Alice",
  "age": 25,
  "city": "Paris"
}

Key features:

  • Guarantees valid JSON - No parsing errors
  • Flexible structure - Model decides format based on context
  • No schema required - Just set the mode
  • Prompt-driven - Describe desired structure in your prompt

Use when:

  • Need valid JSON but structure varies by request
  • Quick prototyping and experimentation
  • Structure is implied by prompt context
  • Building dynamic responses where schema isn't fixed

Example use cases:

# Dynamic data extraction
response = requests.post(
    "https://api.aitronos.com/v1/model/response",
    headers={"X-API-Key": api_key},
    json={
        "organization_id": "org_abc123",
        "model": "ftg-3.0",
        "output_mode": "json",
        "inputs": [{
            "role": "user",
            "texts": [{
                "text": "Analyze this product review and return sentiment, key points, and rating: 'Great product! Fast shipping. Would buy again. 5 stars.'"
            }]
        }]
    }
)

# Returns flexible JSON like:
# {
#   "sentiment": "positive",
#   "key_points": ["great product", "fast shipping", "would repurchase"],
#   "rating": 5,
#   "recommendation": "highly recommended"
# }

3. json_schema Mode

Most powerful option - define exact structure using JSON Schema. The model guarantees output matching your schema.

{
  "organization_id": "org_abc123",
  "model": "ftg-3.0",
  "output_mode": "json_schema",
  "json_schema": {
    "id": "user_profile_v1",
    "description": "User profile extraction schema",
    "strict": true,
    "schema": {
      "type": "object",
      "properties": {
        "name": {"type": "string"},
        "age": {"type": "integer"},
        "city": {"type": "string"}
      },
      "required": ["name", "age", "city"],
      "additionalProperties": false
    }
  },
  "inputs": [
    {
      "role": "user",
      "texts": [{"text": "Extract: 'Alice is 25 and lives in Paris'"}]
    }
  ]
}

Response (guaranteed structure):

{
  "name": "Alice",
  "age": 25,
  "city": "Paris"
}

Key features:

  • 100% schema compliance - No exceptions, guaranteed structure
  • Type safety - Integers are integers, strings are strings, no type coercion
  • Validation - Required fields always present, no missing data
  • Predictable - Same structure every time, perfect for automation
  • Multiple schemas - Provide multiple schemas; model selects appropriate one

Use when:

  • Type safety is critical for your application
  • Integrating with typed languages (TypeScript, Go, Rust, Java)
  • Direct database insertions without validation
  • Production API responses requiring consistency
  • Compliance and audit requirements

JSON Schema

Basic Schema Example

{
  "id": "simple_schema",
  "schema": {
    "type": "object",
    "properties": {
      "task": {"type": "string"},
      "completed": {"type": "boolean"},
      "priority": {"type": "integer", "minimum": 1, "maximum": 5}
    },
    "required": ["task", "completed"]
  }
}

Supported Types

Primitives

{
  "type": "object",
  "properties": {
    "text": {"type": "string"},
    "count": {"type": "integer"},
    "price": {"type": "number"},
    "active": {"type": "boolean"},
    "data": {"type": "null"}
  }
}

Arrays

{
  "type": "object",
  "properties": {
    "tags": {
      "type": "array",
      "items": {"type": "string"}
    },
    "scores": {
      "type": "array",
      "items": {"type": "integer"},
      "minItems": 1,
      "maxItems": 10
    }
  }
}

Nested Objects

{
  "type": "object",
  "properties": {
    "user": {
      "type": "object",
      "properties": {
        "name": {"type": "string"},
        "email": {"type": "string", "format": "email"}
      },
      "required": ["name", "email"]
    }
  }
}

Enums

{
  "type": "object",
  "properties": {
    "status": {
      "type": "string",
      "enum": ["pending", "in_progress", "completed", "failed"]
    },
    "priority": {
      "type": "string",
      "enum": ["low", "medium", "high"]
    }
  }
}

Advanced Schema Features

Constraints

{
  "type": "object",
  "properties": {
    "username": {
      "type": "string",
      "minLength": 3,
      "maxLength": 20,
      "pattern": "^[a-zA-Z0-9_]+$"
    },
    "age": {
      "type": "integer",
      "minimum": 0,
      "maximum": 120
    },
    "email": {
      "type": "string",
      "format": "email"
    },
    "website": {
      "type": "string",
      "format": "uri"
    }
  }
}

Optional vs Required

{
  "type": "object",
  "properties": {
    "id": {"type": "string"},           // Required (in required array)
    "name": {"type": "string"},         // Required
    "description": {"type": "string"}   // Optional (not in required array)
  },
  "required": ["id", "name"],
  "additionalProperties": false          // Reject unknown properties
}

Complex Example: E-commerce Order

{
  "id": "order_schema_v2",
  "strict": true,
  "schema": {
    "type": "object",
    "properties": {
      "orderId": {"type": "string"},
      "customer": {
        "type": "object",
        "properties": {
          "name": {"type": "string"},
          "email": {"type": "string", "format": "email"},
          "address": {
            "type": "object",
            "properties": {
              "street": {"type": "string"},
              "city": {"type": "string"},
              "zipCode": {"type": "string"}
            },
            "required": ["street", "city", "zipCode"]
          }
        },
        "required": ["name", "email", "address"]
      },
      "items": {
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "productId": {"type": "string"},
            "name": {"type": "string"},
            "quantity": {"type": "integer", "minimum": 1},
            "price": {"type": "number", "minimum": 0}
          },
          "required": ["productId", "name", "quantity", "price"]
        },
        "minItems": 1
      },
      "total": {"type": "number", "minimum": 0},
      "status": {
        "type": "string",
        "enum": ["pending", "processing", "shipped", "delivered", "cancelled"]
      }
    },
    "required": ["orderId", "customer", "items", "total", "status"],
    "additionalProperties": false
  }
}

Use Cases

1. Data Extraction

Extract structured information from unstructured text:

response = requests.post(
    'https://api.aitronos.com/v1/model/response',
    json={
        "model": "gpt-4.1",
        "outputMode": "json_schema",
        "jsonSchema": {
            "id": "contact_extraction",
            "schema": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"},
                    "email": {"type": "string"},
                    "phone": {"type": "string"},
                    "company": {"type": "string"}
                },
                "required": ["name"]
            }
        },
        "inputs": [{
            "role": "user",
            "texts": [{"text": "Extract contact: John Doe, john@example.com, works at Acme Corp"}]
        }]
    }
)

2. API Response Generation

Generate API-ready responses:

const response = await fetch('/v1/model/response', {
  method: 'POST',
  body: JSON.stringify({
    model: 'gpt-4.1',
    outputMode: 'json_schema',
    jsonSchema: {
      id: 'api_response',
      schema: {
        type: 'object',
        properties: {
          success: { type: 'boolean' },
          data: {
            type: 'object',
            properties: {
              userId: { type: 'string' },
              message: { type: 'string' }
            }
          },
          error: {
            type: 'object',
            properties: {
              code: { type: 'string' },
              message: { type: 'string' }
            }
          }
        },
        required: ['success']
      }
    },
    inputs: [{
      role: 'user',
      texts: [{ text: 'Generate success response for user creation' }]
    }]
  })
});

3. Database Record Generation

Create type-safe database entries:

# Generate database record
schema = {
    "id": "user_record",
    "schema": {
        "type": "object",
        "properties": {
            "id": {"type": "string"},
            "username": {"type": "string"},
            "email": {"type": "string", "format": "email"},
            "role": {"type": "string", "enum": ["user", "admin", "moderator"]},
            "created_at": {"type": "string", "format": "date-time"},
            "active": {"type": "boolean"}
        },
        "required": ["id", "username", "email", "role", "active"]
    }
}

result = generate_response(outputMode="json_schema", jsonSchema=schema)

# Direct database insertion (no validation needed!)
db.users.insert_one(result)

4. TypeScript Integration

Generate type-safe responses for TypeScript:

// Define TypeScript interface
interface UserProfile {
  id: string;
  name: string;
  age: number;
  email: string;
  interests: string[];
}

// Matching JSON Schema
const schema = {
  id: 'user_profile',
  schema: {
    type: 'object',
    properties: {
      id: { type: 'string' },
      name: { type: 'string' },
      age: { type: 'integer' },
      email: { type: 'string', format: 'email' },
      interests: { type: 'array', items: { type: 'string' } }
    },
    required: ['id', 'name', 'age', 'email', 'interests']
  }
};

// API call with type safety
const response = await createResponse({
  outputMode: 'json_schema',
  jsonSchema: schema,
  inputs: [...]
});

const user: UserProfile = response.output[0].content[0].data; // Type-safe!

Comparison: JSON vs JSON Schema

Featurejson Modejson_schema Mode
Valid JSON✅ Guaranteed✅ Guaranteed
Structure control❌ Model decides✅ You define exactly
Type enforcement❌ Best effort✅ Strict (int vs string)
Required fields❌ May be missing✅ Always present
Schema validation❌ None✅ 100% compliant
Setup complexitySimple (just set mode)Requires schema definition
Flexibility✅ Adapts to context❌ Fixed structure
Use casePrototyping, dynamicProduction, type-safe
PerformanceFastSlightly slower (~5-10%)

When to Use Each Mode

Use json Mode When:

Prototyping - Quick experiments without schema overhead
Dynamic structures - Output format varies by request
Exploratory analysis - Discovering what structure works best
Flexible responses - Model adapts structure to content
Simple extraction - Basic data extraction tasks

Example scenarios:

  • Analyzing customer feedback (structure varies by feedback type)
  • Extracting entities from diverse documents
  • Building chatbots with dynamic response formats
  • Quick data transformations

Use json_schema Mode When:

Production systems - Type safety prevents runtime errors
Database operations - Direct insertion without validation
API integrations - Consistent structure for downstream systems
Typed languages - TypeScript, Go, Rust, Java integration
Compliance - Audit trails require predictable formats
Critical data - Financial, medical, legal applications

Example scenarios:

  • E-commerce order processing
  • Medical record extraction
  • Financial transaction parsing
  • CRM data synchronization
  • Automated report generation

Real-World Examples

Example 1: Customer Support Ticket Analysis (JSON Mode)

import requests
import os

# Analyze support tickets with flexible structure
response = requests.post(
    "https://api.aitronos.com/v1/model/response",
    headers={"Authorization": f"Bearer {os.environ['FREDDY_API_KEY']}"},
    json={
        "organization_id": "org_abc123",
        "model": "ftg-3.0",
        "output_mode": "json",
        "inputs": [{
            "role": "user",
            "texts": [{
                "text": """Analyze this support ticket and extract key information:
                
                Subject: Login issues on mobile app
                From: john@example.com
                Message: I can't log into the iOS app. It keeps saying 'invalid credentials' 
                but I'm sure my password is correct. This started yesterday after the update.
                
                Return JSON with: issue_type, priority, affected_platform, user_email, 
                suggested_solution, and any other relevant fields."""
            }]
        }]
    }
)

result = response.json()
ticket_data = json.loads(result["output"][0]["content"][0]["text"])

# Model returns flexible structure:
# {
#   "issue_type": "authentication",
#   "priority": "high",
#   "affected_platform": "iOS",
#   "user_email": "john@example.com",
#   "suggested_solution": "Password reset or cache clear",
#   "started_after": "app update",
#   "error_message": "invalid credentials"
# }

Example 2: E-commerce Order Processing (JSON Schema Mode)

# Define strict schema for order processing
order_schema = {
    "id": "order_processing_v1",
    "description": "E-commerce order extraction schema",
    "strict": True,
    "schema": {
        "type": "object",
        "properties": {
            "order_id": {"type": "string"},
            "customer": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"},
                    "email": {"type": "string", "format": "email"},
                    "phone": {"type": "string"}
                },
                "required": ["name", "email"]
            },
            "items": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "product_id": {"type": "string"},
                        "name": {"type": "string"},
                        "quantity": {"type": "integer", "minimum": 1},
                        "price": {"type": "number", "minimum": 0}
                    },
                    "required": ["product_id", "name", "quantity", "price"]
                },
                "minItems": 1
            },
            "total": {"type": "number", "minimum": 0},
            "shipping_address": {
                "type": "object",
                "properties": {
                    "street": {"type": "string"},
                    "city": {"type": "string"},
                    "state": {"type": "string"},
                    "zip": {"type": "string"}
                },
                "required": ["street", "city", "state", "zip"]
            }
        },
        "required": ["order_id", "customer", "items", "total", "shipping_address"],
        "additionalProperties": False
    }
}

response = requests.post(
    "https://api.aitronos.com/v1/model/response",
    headers={"Authorization": f"Bearer {os.environ['FREDDY_API_KEY']}"},
    json={
        "organization_id": "org_abc123",
        "model": "ftg-3.0",
        "output_mode": "json_schema",
        "json_schema": order_schema,
        "inputs": [{
            "role": "user",
            "texts": [{
                "text": """Extract order details from this email:
                
                Order #12345
                Customer: Jane Smith (jane@example.com, 555-0123)
                Items: 
                - Product A123: Wireless Mouse x2 @ $29.99 each
                - Product B456: USB Cable x1 @ $9.99
                Total: $69.97
                Ship to: 123 Main St, Springfield, IL 62701"""
            }]
        }]
    }
)

# Guaranteed structure - safe for direct database insertion
order_data = json.loads(response.json()["output"][0]["content"][0]["text"])
db.orders.insert_one(order_data)  # No validation needed!

Example 3: Resume Parsing (JSON Mode)

// Parse resumes with flexible structure
const response = await fetch('https://api.aitronos.com/v1/model/response', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.FREDDY_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    organization_id: 'org_abc123',
    model: 'ftg-3.0',
    output_mode: 'json',
    inputs: [{
      role: 'user',
      texts: [{
        text: `Parse this resume and extract all relevant information in JSON format:
        
        John Doe
        Software Engineer
        john.doe@email.com | (555) 123-4567 | linkedin.com/in/johndoe
        
        EXPERIENCE
        Senior Developer at Tech Corp (2020-2023)
        - Led team of 5 developers
        - Built microservices architecture
        - Increased performance by 40%
        
        EDUCATION
        BS Computer Science, MIT (2016-2020)
        
        SKILLS
        Python, JavaScript, AWS, Docker, Kubernetes`
      }]
    }]
  })
});

const result = await response.json();
const resume = JSON.parse(result.output[0].content[0].text);

// Model adapts structure to resume content:
// {
//   "name": "John Doe",
//   "title": "Software Engineer",
//   "contact": {
//     "email": "john.doe@email.com",
//     "phone": "(555) 123-4567",
//     "linkedin": "linkedin.com/in/johndoe"
//   },
//   "experience": [...],
//   "education": [...],
//   "skills": [...]
// }

Best Practices

✅ DO

  • Use json_schema for production - Type safety prevents runtime errors
  • Use json for prototyping - Faster iteration without schema overhead
  • Version schema IDs - user_profile_v2 for tracking changes
  • Set additionalProperties: false - Reject unexpected fields in schemas
  • Use enums for fixed values - Ensures valid options (status, priority, etc.)
  • Document your schemas - Add descriptions for maintainability
  • Test schemas - Validate with sample prompts before deployment
  • Describe desired structure in prompts - For JSON mode, be explicit about fields

❌ DON'T

  • Over-constrain schemas - Too strict may fail to generate
  • Use json mode for typed systems - Use json_schema for type safety
  • Forget required fields - Model won't generate without them
  • Nest too deeply - Keep schemas under 5 levels deep
  • Skip ID fields - Always provide schema IDs for caching
  • Mix modes - Choose one approach per use case
  • Ignore validation errors - Always handle schema validation failures

Troubleshooting

Schema Validation Errors

{
  "error": {
    "type": "invalid_request_error",
    "message": "Invalid JSON Schema: property 'age' must be of type 'integer'",
    "code": "invalid_schema"
  }
}

Solution: Validate your schema against JSON Schema Draft 2020-12 specification.

Missing Required Fields

{
  "error": {
    "type": "invalid_request_error",
    "message": "jsonSchema is required when outputMode is 'json_schema'",
    "code": "missing_parameter",
    "param": "jsonSchema"
  }
}

Solution: Always provide jsonSchema when using json_schema mode.

Schema Too Complex

If the model struggles to generate output matching a complex schema:

  • Simplify nested structures
  • Remove overly strict constraints
  • Split into multiple requests
  • Use strict: false for best-effort compliance

FAQ

Q: Can I use json mode without a schema?
A: Yes! json mode returns valid JSON without requiring a schema. Just set output_mode: "json" and describe the desired structure in your prompt.

Q: What's the difference between json and json_schema mode?
A: json mode guarantees valid JSON but lets the model decide the structure. json_schema mode enforces an exact structure you define. Use json for flexibility, json_schema for type safety.

Q: Will JSON mode always return valid JSON?
A: Yes, both json and json_schema modes guarantee syntactically valid JSON that can be parsed without errors.

Q: What JSON Schema version is supported?
A: JSON Schema Draft 2020-12.

Q: Is json_schema mode slower than json mode?
A: Slightly (~5-10%), but negligible compared to the value of guaranteed structure and type safety.

Q: Can the model fail to generate valid output?
A: With strict: true, the model always produces valid schema-compliant output. With strict: false, it makes best effort. In json mode, the model always returns valid JSON.

Q: How do I handle optional fields?
A: In json_schema mode, don't include them in the required array. In json mode, mention in your prompt which fields are optional.

Q: Can I use multiple schemas in one request?
A: Yes! Provide multiple schemas in the json_schema parameter. The model will select the most appropriate one based on context.

Q: What happens if my prompt conflicts with the schema?
A: The schema takes precedence. The model will always conform to the schema structure, even if the prompt suggests otherwise.

Q: Can I use JSON mode with streaming?
A: Yes, both json and json_schema modes work with streaming. The JSON will be streamed incrementally.

Q: How do I debug schema validation errors?
A: Check that your schema follows JSON Schema Draft 2020-12 specification. Use online validators like jsonschemavalidator.net to test your schema.


Related: