Recipe 03

Structured outputs

The old trick was prefilling {"name": " in an assistant turn. That returns 400 now — and the replacement is strictly better.

Pydantic + parse (recommended)

from pydantic import BaseModel

class ContactInfo(BaseModel):
    name: str
    email: str
    plan: str
    demo_requested: bool

response = client.messages.parse(
    model="claude-fable-5",
    max_tokens=16000,
    messages=[{"role": "user", "content": "Extract: Jane Doe (jane@co.com) wants Enterprise, wants a demo."}],
    output_format=ContactInfo,
)
contact = response.parsed_output  # a validated ContactInfo instance
print(contact.name)  # "Jane Doe"

Raw schema

When you don't want Pydantic, pass the schema directly. Note it's output_config.format — the older top-level output_format param on create() is deprecated:

response = client.messages.create(
    model="claude-fable-5",
    max_tokens=16000,
    messages=[{"role": "user", "content": document}],
    output_config={"format": {
        "type": "json_schema",
        "schema": {
            "type": "object",
            "properties": {"name": {"type": "string"}, "email": {"type": "string"}},
            "required": ["name", "email"],
            "additionalProperties": False,
        },
    }},
)

Strict tool parameters

Add "strict": true to a tool definition and the API guarantees the tool call's input matches your schema — no more hand-validating enum fields.

Limits worth knowing

  • Every object needs additionalProperties: false. No recursive schemas; no numeric/string constraints like minimum or maxLength (the Python/TS SDKs strip those and validate client-side).
  • First request with a new schema pays a one-time compilation cost; it's cached for 24 hours after.
  • Incompatible with citations. If stop_reason is max_tokens, the JSON may be truncated — raise the cap.

Moral: never parse what you can guarantee.