Structured Output Field Types and Constraints Reference
Not every JSON Schema construct is enforced by output_config.format. This page is a quick-reference table for what's supported, what isn't, and what happens when you use an unsupported construct anyway.
Check this before designing a schema with anything beyond basic types, enum, and simple nesting.
How to Use This Reference
- Scan the Supported table first when designing a new schema - stick to these constructs for the strongest guarantee.
- Scan the Not Supported table before adding a constraint like a numeric range or string length limit - it will not do what you expect.
- When a construct isn't supported, use the SDK's client-side handling (below) or move the constraint into your own post-validation step.
- Revisit this page any time a schema you expect to be enforced doesn't seem to be behaving as constrained.
Supported Constructs
| Construct | Example | Notes |
|---|---|---|
| Basic types | "type": "object", "array", "string", "integer", "number", "boolean", "null" | The foundation of every schema; combine freely. |
enum | {"type": "string", "enum": ["low", "medium", "high"]} | Restricts a field to a fixed set of values - the standard tool for classification-style fields. |
const | {"const": "invoice"} | Pins a field to exactly one literal value. |
anyOf | {"anyOf": [{"type": "string"}, {"type": "integer"}]} | Allows a field to match any one of several sub-schemas. |
allOf | {"allOf": [{"$ref": "#/$defs/Base"}, {"properties": {...}}]} | Combines multiple sub-schemas that must all match. |
$ref / $def | {"$ref": "#/$defs/Address"} | Standard schema composition/reuse - what Pydantic emits for nested models. |
String format | date-time, time, date, duration, email, hostname, uri, ipv4, ipv6, uuid | Constrains a string's shape without a custom regex. |
additionalProperties: false | {"type": "object", "additionalProperties": False} | Required on every object in the schema, not optional. |
schema = {
"type": "object",
"properties": {
"id": {"type": "string", "format": "uuid"},
"status": {"type": "string", "enum": ["open", "closed"]},
"created_at": {"type": "string", "format": "date-time"},
},
"required": ["id", "status", "created_at"],
"additionalProperties": False,
}Not Supported
| Construct | Example (will not be enforced) | What to do instead |
|---|---|---|
| Recursive schemas | An object whose properties reference itself (directly or via $ref cycles) | Flatten the shape, or split the extraction into multiple sequential calls. |
| Numeric range constraints | minimum, maximum, multipleOf | Validate the number range yourself after parsing (a Pydantic field_validator is a natural place for this). |
| String length constraints | minLength, maxLength | Validate string length yourself after parsing. |
| Complex array constraints | e.g. minItems, maxItems, uniqueItems in combination with other array keywords | Validate array shape/length yourself after parsing. |
additionalProperties set to anything other than false | "additionalProperties": true or a schema object | Always use additionalProperties: false - this is the only value the API's enforcement supports. |
from pydantic import BaseModel, field_validator
class Invoice(BaseModel):
total: float
@field_validator("total")
@classmethod
def total_in_range(cls, v: float) -> float:
# The schema itself cannot enforce this range - do it here instead.
if not (0 <= v <= 1_000_000):
raise ValueError("total out of expected range")
return vSDK Handling of Unsupported Constraints
| SDK | Behavior |
|---|---|
Python (anthropic) | Automatically removes unsupported constraints from the schema before sending it to the API, then validates them client-side against the response. |
TypeScript (@anthropic-ai/sdk) | Same automatic stripping-and-client-side-validation behavior as the Python SDK. |
# You can still write minLength in your schema for documentation/intent -
# the SDK strips it before the request goes out and checks it client-side
# once the response comes back, rather than silently ignoring it end-to-end.
schema = {
"type": "object",
"properties": {"code": {"type": "string", "minLength": 4}},
"required": ["code"],
"additionalProperties": False,
}Object Requirements at a Glance
| Rule | Applies to | Consequence if skipped |
|---|---|---|
required lists every property you need present | Every object in the schema, including nested ones | Omitted fields become optional; the model may leave them out. |
additionalProperties: false | Every object in the schema, including nested ones | The object is left open to extra, unrequested fields. |
| Shallow nesting (2-3 levels) | The schema as a whole | Deep or recursive structures risk hitting unsupported territory. |
Gotchas
- Adding
minimum/maximumand assuming the API rejects out-of-range values. It doesn't enforce this - the field will validate as long as it's the right numeric type. Fix: add a Pydanticfield_validator(or equivalent) to check the range after parsing. - Setting
additionalPropertiesto a schema object to allow "extra fields of a certain type." Only literalfalseis supported. Fix: if you need flexible extra fields, model them explicitly as a known property (e.g. ametadataobject) rather than relying onadditionalProperties. - Designing a self-referential schema for tree-like data. Recursive schemas aren't supported. Fix: cap the depth explicitly in the schema (e.g.
childrenas a fixed-depth nested type) or represent the tree as a flat list with parent references instead.
FAQs
What happens if I use minLength in my schema?
- The Python and TypeScript SDKs strip it from the schema sent to the API and instead validate it client-side once the response arrives.
- It is not enforced by the API's generation-time constraint - treat it as a client-side check, not a generation-time guarantee.
Can I express "one of a fixed set of strings" in a supported way?
{"type": "string", "enum": ["draft", "sent", "paid"]}- Yes -
enumis fully supported and is the standard way to constrain a field to fixed values.
Is additionalProperties always required?
- Yes - every object in the schema must set
additionalProperties: false. There is no supported value other thanfalse.
Can I nest objects three or four levels deep?
- Two to three levels is reliable. Very deep nesting increases the risk of hitting an unsupported construct or a schema the API rejects.
- Prefer flattening deeply nested shapes where the data allows it.
Are recursive schemas rejected outright, or just not enforced?
- Recursive schemas are not supported for this feature - avoid a property referencing its own containing type, directly or through a
$refcycle.
Does $ref work the way it does in standard JSON Schema?
- Yes -
$ref/$defcomposition is supported, and it's exactly what Pydantic'smodel_json_schema()generates for nestedBaseModeltypes.
Can I validate numeric ranges some other way?
- Yes - since
minimum/maximum/multipleOfaren't enforced, add a Pydanticfield_validatoror a manual check afterclient.messages.parse()returns.
Are string formats like email and uuid actually enforced?
- Yes -
formatvalues includingemail,uuid,date-time,date,time,duration,hostname,uri,ipv4, andipv6are supported.
What's the safest subset of JSON Schema to design around?
- Basic types,
enum,const,anyOf/allOf,$ref/$def, supported stringformatvalues, plusrequiredandadditionalProperties: falseon every object. - Anything outside that list should be treated as unverified until you check this page.
Does an unsupported constraint cause a request error?
- Not necessarily - the Python and TypeScript SDKs strip unsupported constraints automatically rather than erroring, then validate them client-side.
- Don't rely on an error to tell you a constraint isn't working; check this reference instead.
Can arrays have constrained lengths?
- Complex array constraints such as
minItems/maxItems/uniqueItemsare not supported. - Validate array length or uniqueness yourself after the response comes back.
Related
- Defining a JSON Schema for output_config.format - the required/additionalProperties conventions this reference assumes.
- Validating and Parsing Structured Responses in Python - where client-side validation of unsupported constraints fits into your code.
- Handling Malformed or Truncated JSON Output - a related but separate failure mode from unsupported schema constructs.
Stack versions: Written against the Claude model lineup current as of ~June 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (the default), and Claude Haiku 4.5 - and the official
anthropicPython SDK (latest 0.x release). Model names, SDK versions, and pricing move quickly - verify current specifics at platform.claude.com/docs before relying on them.