All guides

Original explainer

Ollama structured outputs: pass a JSON Schema in format, then validate the reply

Pass a JSON Schema in the format field of /api/chat, then parse and validate message.content on the client before anything is written downstream.

AI Nav CityWritten / Updated Sources checked

Getting a local model to emit JSON is easy; the hard part is whether downstream steps can trust it. This guide extracts three fields, title, severity and steps, from a synthetic incident note and validates them before anything is written. You need a model usable with /api/chat on a locally running Ollama; the official page notes that Ollama cloud does not currently support structured outputs. The request is written from that page and sample replies were validated offline. Ollama was not started and no model was called.

Action and acceptance sequence

  1. Write the contract

    Define fields, types and required keys, then produce the JSON Schema.[1]

  2. Build the request

    Put the Schema in format and set stream to false.[1]

  3. Parse and validate

    Parse message.content, validate it against the Schema, and write nothing on failure.[1]

Check each stage separately; the final section states execution scope.

Write the schema and required fields

The original exercise outputs one incident record: title is a string, severity is limited to low, medium or high, and steps is an array of strings, all three required. The official approach is to pass a JSON Schema in the format field and, ideally, also include the schema as a string in the prompt to ground the response. Python can use Pydantic model_json_schema() and JavaScript can use Zod z.toJSONSchema(), so one definition both generates the schema and drives later validation.[1]

Build the /api/chat request

The body has four fields: model, messages, stream and format. Set stream to false and put the Schema object in format. That is the shape of the official curl example, which uses the model name gpt-oss; whether that model exists on your machine was not checked, so substitute one you have downloaded. The page also suggests setting temperature to 0 for more deterministic output, but demonstrates it only through options in a Python example. How to place it in a REST body is not shown here, so confirm on the API reference before adding it. With format set to the string json there is no field definition, so you must validate fields yourself.[1]

message.content is a string: parse, then validate

The official Python and JavaScript examples read message.content first and then parse it with model_validate_json or Zod parse, which shows the JSON arrives inside a string you must parse. Eight synthetic reply checks were run offline with Ajv: a conforming reply passes; a missing steps, a severity of urgent, and a number inside steps are rejected; truncated JSON and JSON wrapped in a code fence fail at the parse stage. The example schema has no additionalProperties: false, so an extra note field still passes; add that line to the client-side schema when downstream steps need strictness.[1]

Failure handling and scope

When parsing or validation fails, or a required field is missing, write nothing downstream and do not patch fields by hand on the original reply; record the raw response and retry or hand it to a person. The exercise schema uses enum, while the official examples use Pydantic Literal fields, and whether the server strictly enforces enum was not verified, so client-side validation stays necessary. Offline checks covered the curl script syntax with bash, the request body JSON and the sample replies. Ollama was not run, and nothing here shows the extraction accuracy of any model.[1]

Article sources

Original editorial content; official descriptions and checking suggestions are distinguished.

  1. Ollama · Official sources
    View details