# Structured outputs in FastAPI via responses.parse

Structured Outputs guarantees the model returns JSON matching your schema. In
Python the shortest correct path is the parse helper, verified 2026-09-26.

## The call

```python
from pydantic import BaseModel
from openai import OpenAI

client = OpenAI()

class CalendarEvent(BaseModel):
    name: str
    date: str
    participants: list[str]

response = client.responses.parse(
    model="...",
    input=[{"role": "user", "content": "Alice and Bob are going to a science fair on Friday."}],
    text_format=CalendarEvent,
)
event = response.output_parsed  # a CalendarEvent, not a string
```

FastAPI can return `event` directly; it is already a Pydantic model.

## Schema rules that 400

- Strict mode is the default posture: every field must be required or nullable.
  `Optional[str]` without a default still needs handling; make truly-optional
  fields `str | None = None` so the schema is total.
- The parse helper raises on refusal or schema mismatch instead of returning
  half-JSON. Catch it and return a 502/429-style retryable error, not a 200 with
  an empty body.
- Do not `json.loads(response.output_text)` yourself when you used parse. You get
  a typed object; re-parsing the text throws away the guarantee.

## Check before you ship

- Send input that forces a refusal-adjacent edge (empty prompt) and confirm your
  error path, not a 500.
- Log `response.usage` on parse calls; structured outputs do not change pricing,
  but retries on 400s do change the bill.