Forcing Strict JSON Output
Turn a chatty model into a dependable component that returns parseable data every time.
A model that answers in prose is a demo. A model that answers in a fixed JSON shape is a component you can build on — because a program can consume it without a human in the loop.
This lab walks the distance between the two. Each step has a button that loads the example straight into the editor on the right; press Run to see what comes back.
Step 1 — See the problem
Start with the naive version. Load this and run it:
Extract the person's name, role, and company from this text: Priya Raman has just been promoted to VP of Engineering at Northwind Logisticsafter four years leading their platform team.You will get the right information. You will also get a sentence wrapped around it, or a bulleted list, or a JSON object with a friendly preamble above it — and the exact shape may differ between runs.
Run it two or three times. The variation is the point. Any parser you write against that output is one rephrasing away from breaking.
Step 2 — Ask for JSON (and see why asking isn't enough)
The obvious fix is to ask. Load this version:
Extract the person's name, role, and company from the text below.Return the result as JSON. Priya Raman has just been promoted to VP of Engineering at Northwind Logisticsafter four years leading their platform team.Better — you will almost certainly get JSON. But notice what is still undecided:
- Are the keys
name,full_name, orpersonName? - Is the role
"VP of Engineering"or"Vice President of Engineering"? - Is the JSON wrapped in a
```jsonfence? - Is there a sentence before it?
You have constrained the format and left the schema to chance. That is the gap that breaks pipelines in production.
Step 3 — Specify the schema
Now name the exact keys and types, and forbid everything else. This is the version worth remembering:
Three things are doing the work here:
- Explicit keys. The model no longer invents naming conventions.
- Explicit types.
numbertells it4, not"four years". - Explicit prohibitions. "No markdown fence, no preamble" removes the two most common wrappers.
Run it a few times. The output should now be byte-stable apart from whitespace.
Step 4 — Move the rules into the system prompt
Everything in Step 3 is a durable rule — it applies to every extraction you will ever run, not just this one. That makes it system-prompt material.
Load this into the system prompt buffer (switch to the System prompt tab after pressing the button):
You are a structured data extraction service. Rules that apply to every request:- Respond with a single JSON object and nothing else.- Never wrap the response in a markdown code fence.- Never add explanation, preamble, or trailing commentary.- Use null for any field the source text does not state. Never guess.- Preserve the source's own wording for names, titles, and companies.Then reduce the user turn to just the task and the data:
Schema:{ "name": string, "role": string, "company": string, "tenure_years": number | null} Text:Priya Raman has just been promoted to VP of Engineering at Northwind Logisticsafter four years leading their platform team.Same output, but the request is now half the size and the rules are stated once instead of being copy-pasted into every call. On a system that makes thousands of these calls, that difference is real money and far fewer inconsistencies.
Step 5 — Prove it generalizes
A schema that only works on the sentence you designed it around is not a schema. Swap the data and leave everything else alone:
--- Also extract from this second passage, returning a JSON array of objects --- Marcus Webb joined Halden Bioworks as Chief Data Officer last spring.Aiko Tanaka has been at Meridian Freight for eleven years and now runstheir analytics group.Note this button appends rather than replacing, so your existing prompt stays put. Run it and check three things:
- Does Marcus Webb's
tenure_yearscome back asnull? ("last spring" is not a number of years — guessing here would be a real bug.) - Is Aiko Tanaka's role expressed in the source's own words?
- Did you get an array, because you asked for one?
Step 6 — Skip the prompt engineering entirely
Prompted JSON is a convention the model is following. Most providers now offer something stronger: a schema you pass as a parameter, which the API enforces during generation, so invalid output is structurally impossible.
- Anthropic —
output_config.formatwith ajson_schema - OpenAI — structured outputs with a
json_schemaresponse format - Google —
responseSchemain the generation config
The prompting techniques in Steps 3 through 5 still matter: they are what you
use when a provider lacks the feature, when you are working through a gateway
that strips it, and when you need to explain to a model why a field should be
null rather than merely that it must be a number. But when the parameter is
available, use it — a guarantee beats an instruction.
What to take away
- Asking for "JSON" constrains the format and leaves the schema open.
- Name every key, give every type, and forbid the wrappers explicitly.
- Durable rules belong in the system prompt; the payload belongs in the user turn.
- Require
nullinstead of a guess, or the model will fill gaps confidently. - If your provider enforces schemas natively, prefer that over prompting.
Forcing Strict JSON Output
Turn a chatty model into a dependable component that returns parseable data every time.
A model that answers in prose is a demo. A model that answers in a fixed JSON shape is a component you can build on — because a program can consume it without a human in the loop.
This lab walks the distance between the two. Each step has a button that loads the example straight into the editor on the right; press Run to see what comes back.
Step 1 — See the problem
Start with the naive version. Load this and run it:
Extract the person's name, role, and company from this text: Priya Raman has just been promoted to VP of Engineering at Northwind Logisticsafter four years leading their platform team.You will get the right information. You will also get a sentence wrapped around it, or a bulleted list, or a JSON object with a friendly preamble above it — and the exact shape may differ between runs.
Run it two or three times. The variation is the point. Any parser you write against that output is one rephrasing away from breaking.
Step 2 — Ask for JSON (and see why asking isn't enough)
The obvious fix is to ask. Load this version:
Extract the person's name, role, and company from the text below.Return the result as JSON. Priya Raman has just been promoted to VP of Engineering at Northwind Logisticsafter four years leading their platform team.Better — you will almost certainly get JSON. But notice what is still undecided:
- Are the keys
name,full_name, orpersonName? - Is the role
"VP of Engineering"or"Vice President of Engineering"? - Is the JSON wrapped in a
```jsonfence? - Is there a sentence before it?
You have constrained the format and left the schema to chance. That is the gap that breaks pipelines in production.
Step 3 — Specify the schema
Now name the exact keys and types, and forbid everything else. This is the version worth remembering:
Three things are doing the work here:
- Explicit keys. The model no longer invents naming conventions.
- Explicit types.
numbertells it4, not"four years". - Explicit prohibitions. "No markdown fence, no preamble" removes the two most common wrappers.
Run it a few times. The output should now be byte-stable apart from whitespace.
Step 4 — Move the rules into the system prompt
Everything in Step 3 is a durable rule — it applies to every extraction you will ever run, not just this one. That makes it system-prompt material.
Load this into the system prompt buffer (switch to the System prompt tab after pressing the button):
You are a structured data extraction service. Rules that apply to every request:- Respond with a single JSON object and nothing else.- Never wrap the response in a markdown code fence.- Never add explanation, preamble, or trailing commentary.- Use null for any field the source text does not state. Never guess.- Preserve the source's own wording for names, titles, and companies.Then reduce the user turn to just the task and the data:
Schema:{ "name": string, "role": string, "company": string, "tenure_years": number | null} Text:Priya Raman has just been promoted to VP of Engineering at Northwind Logisticsafter four years leading their platform team.Same output, but the request is now half the size and the rules are stated once instead of being copy-pasted into every call. On a system that makes thousands of these calls, that difference is real money and far fewer inconsistencies.
Step 5 — Prove it generalizes
A schema that only works on the sentence you designed it around is not a schema. Swap the data and leave everything else alone:
--- Also extract from this second passage, returning a JSON array of objects --- Marcus Webb joined Halden Bioworks as Chief Data Officer last spring.Aiko Tanaka has been at Meridian Freight for eleven years and now runstheir analytics group.Note this button appends rather than replacing, so your existing prompt stays put. Run it and check three things:
- Does Marcus Webb's
tenure_yearscome back asnull? ("last spring" is not a number of years — guessing here would be a real bug.) - Is Aiko Tanaka's role expressed in the source's own words?
- Did you get an array, because you asked for one?
Step 6 — Skip the prompt engineering entirely
Prompted JSON is a convention the model is following. Most providers now offer something stronger: a schema you pass as a parameter, which the API enforces during generation, so invalid output is structurally impossible.
- Anthropic —
output_config.formatwith ajson_schema - OpenAI — structured outputs with a
json_schemaresponse format - Google —
responseSchemain the generation config
The prompting techniques in Steps 3 through 5 still matter: they are what you
use when a provider lacks the feature, when you are working through a gateway
that strips it, and when you need to explain to a model why a field should be
null rather than merely that it must be a number. But when the parameter is
available, use it — a guarantee beats an instruction.
What to take away
- Asking for "JSON" constrains the format and leaves the schema open.
- Name every key, give every type, and forbid the wrappers explicitly.
- Durable rules belong in the system prompt; the payload belongs in the user turn.
- Require
nullinstead of a guess, or the model will fill gaps confidently. - If your provider enforces schemas natively, prefer that over prompting.