Getting Reliable Structured Output from LLMs Every Time

Stop writing regex to clean up flaky JSON. The three levels of structured output — prompt-and-pray, JSON mode, and strict schema mode — plus the validation step everyone skips.

The moment you try to wire an AI model into real software, you hit the same wall: you need the model's answer as data — a clean object your code can read — not as a friendly paragraph. So you write respond in JSON at the end of your prompt, and it works. Until it doesn't: the model wraps the JSON in ```json fences, or adds a chatty Sure! Here's your data: preamble, or returns a number where you expected a string, or drops a field entirely on the one request out of fifty that happens to matter.

Then you write a regex to strip the fences. Then a try/except around the parse. Then a retry loop. You are now maintaining a small, brittle machine whose entire job is cleaning up after a model that was never actually made to give you reliable data — because you asked it to behave, not constrained it to conform.

In 2026 there's a much better way, and it's worth understanding the levels so you stop fighting this fight.

Three levels of getting structured data

Level 1 — Prompt and pray

This is please respond in JSON in the prompt. It works most of the time, which is exactly what makes it dangerous — it's reliable enough to ship and unreliable enough to page you at 2 a.m.

The problem is fundamental: nothing is enforcing the format. You're making a polite request and hoping the model honors it on every input, including the weird ones. It won't. Prose creeps in, fields drift, types wobble. Use this only for throwaway experiments.

Level 2 — JSON mode (now legacy)

Most providers added a JSON mode that guarantees the output is syntactically valid JSON. That's a real improvement — no more stray prose, no more broken parses. But note the ceiling carefully: JSON mode guarantees valid JSON, not JSON that matches your schema. You'll always get parseable output; you won't always get the fields, names, and types you asked for. As of 2026, this is considered the legacy option — a stepping stone, not the destination.

Level 3 — Strict schema mode (the production default)

This is the one to reach for. You hand the model an explicit JSON Schema and turn on strict mode, and the output is guaranteed to conform to that schema — every field present, every name correct, every type right — because the model isn't politely trying to comply. It's using constrained decoding: at each step, the system only allows tokens that keep the output valid against your schema. Non-conforming output isn't cleaned up after the fact; it's made structurally impossible.

The practical upshot: schema adherence goes from usually to every single time. The minor token overhead is trivial next to never writing another JSON-repair regex. For any data extraction or agent pipeline going to production, strict schema mode is the default. Enable it.

Most SDKs make this pleasant. You define your shape once — typically with a validation library like Pydantic (Python) or Zod (TypeScript) — and pass it as the schema. Many SDKs then offer a parse-style call that sends the schema and validates the response back into a typed object in one step, so what lands in your code is already the object you wanted, not a string you have to interrogate.

Tool and function arguments are the same problem

If you're building agents or tool-using workflows, the model calls your functions — and the arguments it passes are structured data with exactly the same failure modes. A tool that expects { "order_id": "A-1234", "refund_cents": 500 } will eventually receive refund_cents: "500" or a missing order_id if nothing enforces the shape.

The fix is identical: define a schema for each tool's inputs and enable strict argument validation (commonly strict: true). The model's tool calls are then constrained to your schema, so your function receives well-formed arguments instead of defensive-coding around whatever the model felt like sending. In an agent loop, this removes a whole class of silent, hard-to-trace failures.

The caveat that trips everyone up: valid ≠ correct

Here's the trap that catches teams who think strict mode solved everything: schema conformance is not semantic correctness.

Strict mode guarantees the structure — that confidence is a number between 0 and 1, that category is one of your allowed values, that order_id is a present string. It does not guarantee the values are true. A perfectly schema-valid response can still contain a hallucinated order ID, a confidence score the model made up, or a category that's structurally legal but factually wrong.

So strict mode shrinks the problem; it doesn't delete it. You still need semantic validation in your code:

  • Does that order_id actually exist in your database?
  • Is that refund_cents within an allowed range for this customer?
  • Does the extracted date fall in a plausible window?

Think of it as two layers. Strict schema mode handles shape so you never write parsing glue again. Your own validation handles meaning — the checks only your business logic can make. Skipping the second layer because the first one guarantees the output is the most common mistake in production structured-output pipelines.

When strict mode isn't available: prefilling

Sometimes you're on a model or endpoint without native strict schema support. The best fallback is prefilling — you start the model's response for it. Begin the assistant's turn with an open brace { and the model continues from there, which reliably suppresses the preamble and the code fences and gets you straight into the object. It's not as ironclad as constrained decoding, but combined with a clear schema in the prompt and validation on your end, it's a solid stopgap.

A practical recipe

  1. Define the schema once, in a validation library (Pydantic / Zod). This is your single source of truth for the shape.
  2. Use strict schema mode on the API call — and a parse-style helper if your SDK has one, so you get a typed object back, not a string.
  3. For tools/agents, enable strict argument validation on every tool's input schema.
  4. Keep types tight. Prefer enums over free-form strings, bounded numbers over open ones, required fields over optional. Every constraint you encode is an error the model can't make.
  5. Validate meaning after the fact. Check the values against reality — existence, ranges, business rules. Schema-valid is the floor, not the ceiling.
  6. Handle failure explicitly. Even with all of the above, decide what happens when semantic validation fails: retry, flag for review, or fall back. Don't let a plausible-but-wrong value flow silently downstream.

The bottom line

Reliable structured output stopped being a prompting problem and became an architecture problem — and that's good news, because architecture problems have clean solutions. Stop asking the model to behave and start constraining it to conform: define a schema, turn on strict mode, and then validate the meaning yourself. Do that and the flaky JSON-repair machinery disappears from your codebase for good.

For the broader craft this fits into, start with The Complete Guide to Prompt Engineering in 2026, and if you're deciding how much machinery your task even needs, AI Agent vs Workflow vs Single Call will help you right-size it. Ready-to-use prompts live in the prompt library.