Structured Output From LLMs: Getting Data You Can Trust
Last updated:
Why prose is the wrong output for a business system
Most LLM projects start with a chat window and end with a database. Somewhere between the two, someone has to turn a paragraph of friendly text into fields a system can store, and that step is where a surprising number of projects stall.
A model that replies 'The invoice total appears to be around 1,240 pounds, including VAT' has answered the question. It has also given you nothing a finance system can post. You need total: 1240.00, currency: GBP, vat_included: true, every time, in the same shape, or a clear signal that it could not find them.
Structured output is the discipline of getting that shape reliably. It is the foundation under extraction, classification, routing and most agent tools, so it is worth getting right before anything clever is built on top.
The three levels of structured output
There are roughly three ways to ask a model for data, and they are not equally dependable.
| Approach | How it works | Reliability |
|---|---|---|
| Ask nicely in the prompt | Instructions say 'reply in JSON with these keys' | Fine in a demo, breaks on odd inputs: stray text, missing keys, markdown fences |
| JSON mode | The provider guarantees syntactically valid JSON | Always parses, but keys and types can still drift |
| Schema-constrained output or tool calling | You supply a JSON Schema and generation is constrained to it | Shape is guaranteed; values still need checking |
Use the third level wherever your provider supports it, and most major APIs and several open-weight serving stacks now do. It removes an entire class of bugs, the ones where a parser fails at 2am because the model decided to add a helpful sentence before the opening brace.
Designing a schema the model can actually fill
A schema is a prompt in disguise. The field names, descriptions and types all steer what comes back, so write them for a reader who has never seen your business.
- Name fields plainly. delivery_postcode beats dpc. The model reads the names.
- Describe every field. One sentence on what belongs there and what to do if it is absent.
- Use enums for categories. A fixed list of eight allowed values is far more reliable than free text you normalise later.
- Make absence explicit. Allow null and say when to use it. Otherwise the model will fill the field with something plausible.
- Keep nesting shallow. Deeply nested objects fail more often and are harder to validate.
- Add a confidence or evidence field where it matters, such as the exact quote the value came from.
The evidence field is underrated. Asking for the source text alongside each value makes hallucinated values easier to catch, because you can check in code that the quoted text really appears in the document.
Validation: the part that makes it production-grade
A response that matches the schema can still be wrong. The date can be in the future, the total can disagree with the line items, the postcode can be nonsense. Schema validation checks shape. Business validation checks sense.
- Parse and validate against the schema with a library such as Pydantic or Zod
- Run business rules: totals add up, dates fall in a sensible range, IDs exist in your system
- Check evidence: quoted source text is actually present in the input
- On a fixable failure, retry once with the validation error included in the prompt
- On a second failure, or a low-confidence result, route to a human queue
The retry with the error message is cheap and effective. Telling the model 'line items sum to 1,180 but total says 1,240' fixes a good share of arithmetic slips. Beyond one retry, returns diminish quickly and you are mostly paying to be told the same wrong answer again.
What still goes wrong
Even with constrained decoding, there are failure modes worth designing for rather than discovering in production.
- Confident invention. The field is required, the document does not contain it, so the model supplies something. Allowing null fixes most of this.
- Enum squeezing. An input that fits none of the categories gets forced into the nearest one. Add an 'other' or 'unclear' value.
- Truncation. Long outputs hit a token limit mid-object. Set limits generously and treat a cut-off response as a failure, not a partial success.
- Silent format drift. Dates as 03/04 read the American way. Specify ISO formats in the schema description.
A schema guarantees the answer has the right shape. It says nothing about whether the answer is true.
A worked example: purchase orders by email
Take a wholesaler receiving around 250 purchase orders a week as PDF attachments from forty-odd customers, each with its own layout. Illustratively, a schema-constrained extraction with business checks might post most orders automatically and send the rest to a person, who now checks a pre-filled form instead of typing from scratch.
The checks do the heavy lifting. Customer account numbers are matched against the ERP, product codes against the catalogue, and quantities against sensible ranges for that customer. An order that passes all three is far safer to post than one that merely parsed. We cover the wider pipeline in our piece on turning documents into structured data.
How we approach it
When SpiderHunts builds extraction or classification into a system, the schema and the validation layer are written and tested before any prompt tuning happens. We collect a few hundred real examples, define what correct looks like for each field, and measure field-level accuracy rather than a single overall score, because 'the total is right 99% of the time but the delivery date only 80%' is the kind of finding that changes the design.
If this sits inside a wider integration, our AI integration work usually pairs it with a review queue and a feedback loop, so every human correction becomes a new test case. That loop matters more than the choice of model.
Frequently asked questions
What is the difference between JSON mode and structured outputs?
Do I still need validation if the model follows a schema?
Is tool calling the same as structured output?
Can small or open-weight models produce structured output reliably?
Getting prose back when you need fields?
Send us a few real inputs and the fields you need out of them. We will tell you how reliably they can be extracted and what checks the pipeline needs around it.
Related services
What we build for problems like this one