Structured output / JSON mode
Force the model to emit valid, schema-conforming JSON every time. No regex scraping, no “pretty-please return JSON” prompts, no silent parse failures in production.
Quick start
import OpenAI from "openai";
const openai = new OpenAI();
const schema = {
type: "json_schema",
json_schema: {
name: "sentiment",
strict: true,
schema: {
type: "object",
properties: {
sentiment: {
type: "string",
enum: ["positive", "negative", "neutral"],
},
confidence: { type: "number" },
keywords: {
type: "array",
items: { type: "string" },
},
},
required: ["sentiment", "confidence", "keywords"],
additionalProperties: false,
},
},
};
const completion = await openai.chat.completions.create({
model: "gpt-4o",
messages: [
{
role: "system",
content: "Classify sentiment. Return JSON matching the schema.",
},
{ role: "user", content: "I absolutely love this product!" },
],
response_format: schema,
});
const result = JSON.parse(
completion.choices[0].message.content ?? "{}"
);
// result.sentiment === "positive"
// result.confidence === 0.97
// result.keywords === ["love", "product"]How it works
Set response_format to { type: "json_schema", json_schema: { ... } } and the model’s output is constrained to valid JSON that matches your schema. No more, no less.
Under the hood, the API applies constrained sampling — tokens that would violate the schema are masked from the probability distribution. The model literally cannot emit invalid JSON or extra keys.
Strict mode
Set strict: true inside json_schema to guarantee:
- All
requiredfields are present. - No
additionalPropertiesleak through. enumvalues are respected exactly.- Types match — no string where a number is expected.
Without strict mode, the model may occasionally add extra keys or omit optional-but-expected fields. Always enable it for production pipelines.
Legacy: json_object mode
Before json_schema shipped, the only option was response_format: { type: "json_object" }. It guarantees valid JSON but does not enforce a schema. You still need to prompt the model with the shape you want and validate the result yourself.
const completion = await openai.chat.completions.create({
model: "gpt-4o",
messages: [
{
role: "system",
content:
"Return JSON: { \"sentiment\": \"positive\"|\"negative\"|\"neutral\", \"confidence\": number }",
},
{ role: "user", content: "I hate waiting on hold." },
],
response_format: { type: "json_object" },
});Prefer json_schema for new work. It eliminates the prompt-engineering fragility and gives you compile-time–style guarantees at runtime.
Parsing the response
The model returns a standard chat completion. The JSON lives in choices[0].message.content. Always wrap JSON.parse in a try/catch — even with schema enforcement, network errors or truncated streams can yield unparseable strings.
function safeParse<T>(raw: string | null): T | null {
if (!raw) return null;
try {
return JSON.parse(raw) as T;
} catch {
console.error("Failed to parse structured output:", raw.slice(0, 200));
return null;
}
}
const data = safeParse<SentimentResult>(
completion.choices[0].message.content
);
if (!data) {
// fallback: retry, use default, or escalate
}Supported models
json_schema works with gpt-4o, gpt-4o-mini, and the o-series models. Older models (gpt-4, gpt-3.5-turbo) only support the legacy json_object mode.