Skip to main content
Structured outputs let you enforce a JSON schema on model responses so you can reliably extract structured data, describe images, or keep every reply consistent.

Generating structured JSON

curl -X POST http://localhost:11434/api/chat -H "Content-Type: application/json" -d '{
  "model": "gpt-oss",
  "messages": [{"role": "user", "content": "Tell me about Canada in one line"}],
  "stream": false,
  "format": "json"
}'
When using format: "json", always instruct the model to respond in JSON in your prompt for best results.

Generating structured JSON with a schema

Provide a JSON schema to the format field for strictly typed outputs.
It is ideal to also pass the JSON schema as a string in the prompt to ground the model’s response.
curl -X POST http://localhost:11434/api/chat -H "Content-Type: application/json" -d '{
  "model": "gpt-oss",
  "messages": [{"role": "user", "content": "Tell me about Canada."}],
  "stream": false,
  "format": {
    "type": "object",
    "properties": {
      "name": {"type": "string"},
      "capital": {"type": "string"},
      "languages": {
        "type": "array",
        "items": {"type": "string"}
      }
    },
    "required": ["name", "capital", "languages"]
  }
}'

Example: Extract structured data

Define the objects you want returned and let the model populate the fields:
from ollama import chat
from pydantic import BaseModel

class Pet(BaseModel):
  name: str
  animal: str
  age: int
  color: str | None
  favorite_toy: str | None

class PetList(BaseModel):
  pets: list[Pet]

response = chat(
  model='gpt-oss',
  messages=[{'role': 'user', 'content': 'I have two cats named Luna and Loki. Luna is 3 years old, black, and loves feather toys. Loki is 5, orange, and prefers laser pointers.'}],
  format=PetList.model_json_schema(),
)

pets = PetList.model_validate_json(response.message.content)
print(pets)
Output:
PetList(
  pets=[
    Pet(name='Luna', animal='cat', age=3, color='black', favorite_toy='feather toys'),
    Pet(name='Loki', animal='cat', age=5, color='orange', favorite_toy='laser pointers')
  ]
)

Example: Vision with structured outputs

Vision models accept the same format parameter, enabling deterministic descriptions of images:
from ollama import chat
from pydantic import BaseModel
from typing import Literal, Optional

class Object(BaseModel):
  name: str
  confidence: float
  attributes: str

class ImageDescription(BaseModel):
  summary: str
  objects: list[Object]
  scene: str
  colors: list[str]
  time_of_day: Literal['Morning', 'Afternoon', 'Evening', 'Night']
  setting: Literal['Indoor', 'Outdoor', 'Unknown']
  text_content: Optional[str] = None

response = chat(
  model='gemma3',
  messages=[{
    'role': 'user',
    'content': 'Describe this photo and list the objects you detect.',
    'images': ['path/to/image.jpg'],
  }],
  format=ImageDescription.model_json_schema(),
  options={'temperature': 0},
)

image_description = ImageDescription.model_validate_json(response.message.content)
print(image_description)

API parameters

format
string | object
Response format:
  • "json": Return valid JSON (any structure)
  • {...}: JSON schema object for strict typing

Response structure

With structured outputs, the message.content field contains a JSON string matching your schema:
{
  "model": "gpt-oss",
  "created_at": "2024-12-09T21:07:55.186497Z",
  "message": {
    "role": "assistant",
    "content": "{\"name\":\"Canada\",\"capital\":\"Ottawa\",\"languages\":[\"English\",\"French\"]}"
  },
  "done": true
}

Tips for reliable structured outputs

  • Define schemas with type validators: Use Pydantic (Python) or Zod (JavaScript) so they can be reused for validation
  • Lower the temperature: Set temperature: 0 in options for more deterministic completions
  • Include schema in prompt: Mention the expected structure in your prompt for better results
  • Use descriptive field names: Clear field names help the model understand what data to extract
  • Add field descriptions: Include description in your JSON schema for complex fields
  • Test with examples: Provide example outputs in your prompt when schema is complex

OpenAI compatibility

Structured outputs work through the OpenAI-compatible API via response_format:
from openai import OpenAI
from pydantic import BaseModel

client = OpenAI(
  base_url='http://localhost:11434/v1',
  api_key='ollama',
)

class Country(BaseModel):
  name: str
  capital: str
  languages: list[str]

response = client.chat.completions.create(
  model='gpt-oss',
  messages=[{'role': 'user', 'content': 'Tell me about Canada'}],
  response_format={
    'type': 'json_schema',
    'json_schema': {
      'name': 'country_info',
      'schema': Country.model_json_schema()
    }
  }
)

print(response.choices[0].message.content)

Common use cases

Data extraction from text

class Invoice(BaseModel):
  invoice_number: str
  date: str
  total_amount: float
  items: list[dict]

response = chat(
  model='gpt-oss',
  messages=[{'role': 'user', 'content': invoice_text}],
  format=Invoice.model_json_schema()
)

Form filling

class ContactForm(BaseModel):
  name: str
  email: str
  phone: str
  message: str

response = chat(
  model='gpt-oss',
  messages=[{'role': 'user', 'content': user_input}],
  format=ContactForm.model_json_schema()
)

Sentiment analysis

from typing import Literal

class Sentiment(BaseModel):
  sentiment: Literal['positive', 'negative', 'neutral']
  confidence: float
  reasoning: str

response = chat(
  model='gpt-oss',
  messages=[{'role': 'user', 'content': review_text}],
  format=Sentiment.model_json_schema()
)

Limitations

  • Structured outputs may be slower than regular completions
  • Complex schemas may require larger models for best results
  • Some models handle structured outputs better than others
  • Very restrictive schemas might cause the model to refuse certain requests

Build docs developers (and LLMs) love