Structured outputs

When the answer feeds a program rather than a person, ask for JSON. JSON mode makes the model emit a single valid JSON object; a schema in the prompt makes it the object you expect.

JSON mode#

Set response_format to {"type":"json_object"} on any model whose catalog entry has json_mode true. The model then returns a syntactically valid JSON object and nothing else: no prose around it, no code fences.

curl
curl https://api.ahurasense.com/v1/chat/completions \
  -H "Authorization: Bearer $AHURA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-5.4-mini",
    "response_format": {"type": "json_object"},
    "messages": [
      {"role": "system", "content": "Extract the fields. Reply with a JSON object with keys name, email, intent (one of: sales, support, other)."},
      {"role": "user", "content": "Hi, Priya here ([email protected]). My invoice from last month looks wrong."}
    ]
  }'
message.content, parsed
{ "name": "Priya", "email": "[email protected]", "intent": "support" }

Schemas#

JSON mode guarantees valid JSON, not a particular shape. To pin the shape, put the schema in the prompt, in words or as a JSON Schema block, and validate the answer on your side. On backends that support it, a response_format of type json_schema is passed through unchanged and enforces the schema at generation time; where the backend does not support it, the request is refused with upstream_rejected_request, so keep the prompt-plus-validation path as the fallback.

pydantic
from pydantic import BaseModel, EmailStr
from typing import Literal

class Lead(BaseModel):
    name: str
    email: EmailStr
    intent: Literal["sales", "support", "other"]

completion = client.chat.completions.create(
    model="openai/gpt-5.4-mini",
    response_format={"type": "json_object"},
    messages=[
        {"role": "system", "content": f"Reply with JSON matching this schema: {Lead.model_json_schema()}"},
        {"role": "user", "content": text},
    ],
    temperature=0,
)
lead = Lead.model_validate_json(completion.choices[0].message.content)

Tool calling as structured output#

Another reliable route is a single tool whose parameters are the schema you want, with tool_choice forcing that tool. The arguments come back typed by the schema and many models are more accurate this way than with free-form JSON. See Tool calling.

Habits that help#

  • Use temperature: 0. Extraction wants the most likely answer, and it makes repeats cacheable.
  • Give one example of the exact object you want when the shape has nesting.
  • Set a max_tokens large enough for the whole object; a truncated object is not valid JSON.
  • Validate. A schema violation should be a retry with the error appended to the conversation, not a crash.

Something missing or wrong on this page? Tell us, and quote the page title.