JSON Schemas
What a Schema Is
A JSON Schema is itself a JSON document that describes what other JSON documents must look like. It is like a form template with labelled boxes: "order_id: text, required", "rating: whole number", "issue: tick one of late / damaged / wrong item". A schema has a few building blocks:
| Keyword | Meaning | Example |
|---|---|---|
type | The kind of value | "string", "integer", "boolean", "object", "array" |
properties | The fields of an object and the rule for each | {"rating": {"type": "integer"}} |
required | Fields that must be present | ["order_id", "issue"] |
enum | A fixed list of allowed values | ["late", "damaged", "other"] |
additionalProperties: false | No extra fields allowed (required on every object for Claude structured outputs) | |
items | The rule for each element of an array | {"type": "string"} |
How Schema Enforcement Works
- You send the schema with the request in
output_config.format. - The API prepares the schema once (a one-time cost on the first request; the same schema is then cached for about 24 hours).
- While the model generates, its choices are constrained so that every token keeps the output valid for the schema. It cannot open a field that does not exist or write a word where a number belongs.
- You receive text that is guaranteed to parse and match the schema, except in two cases: the model refused (
stop_reasonis"refusal") or ran out of tokens ("max_tokens"). Always checkstop_reason.
Describing the Shape
JSON Schema is a standard way to describe JSON: which fields, their types, which are required, allowed values. With the Claude API, pass it in output_config={"format": {"type": "json_schema", "schema": ...}} and the reply is guaranteed to be valid JSON matching the schema.
import json
schema = {
"type": "object",
"properties": {
"order_id": {"type": "string"},
"issue": {"type": "string", "enum": ["late", "damaged", "wrong_item", "refund", "other"]},
"rating": {"type": "integer"},
"wants_callback": {"type": "boolean"},
},
"required": ["order_id", "issue", "rating", "wants_callback"],
"additionalProperties": False,
}
response = client.messages.create(
model="claude-haiku-5-5",
max_tokens=500,
output_config={"format": {"type": "json_schema", "schema": schema}},
messages=[{"role": "user", "content": "Order SE-4410 came with a cracked screen guard. 2 stars. Call me back!"}],
)
data = json.loads(next(b.text for b in response.content if b.type == "text"))
print(data)Run it
python schema_extract.pyExample output
{'order_id': 'SE-4410', 'issue': 'damaged', 'rating': 2, 'wants_callback': True}A Mini Schema Validator
Example
Libraries like jsonschema and Pydantic validate for you. Underneath, validation is simple checks like these:
TYPES = {"string": str, "integer": int, "boolean": bool, "number": (int, float)}
def validate(data, schema):
errors = []
for field in schema.get("required", []):
if field not in data:
errors.append(f"missing '{field}'")
for field, value in data.items():
rule = schema["properties"].get(field)
if rule is None:
if schema.get("additionalProperties") is False:
errors.append(f"unexpected '{field}'")
continue
expected = TYPES[rule["type"]]
if not isinstance(value, expected) or (rule["type"] == "integer" and isinstance(value, bool)):
errors.append(f"'{field}' should be {rule['type']}")
if "enum" in rule and value not in rule["enum"]:
errors.append(f"'{field}' must be one of {rule['enum']}")
return errors
schema = {"type": "object", "additionalProperties": False, "required": ["order_id", "issue", "rating"],
"properties": {"order_id": {"type": "string"}, "rating": {"type": "integer"},
"issue": {"type": "string", "enum": ["late", "damaged", "other"]}}}
print(validate({"order_id": "SE-4410", "issue": "damaged", "rating": 2}, schema))
print(validate({"order_id": "SE-4410", "issue": "broken", "rating": "2", "mood": "angry"}, schema))Output
[] ["'issue' must be one of ['late', 'damaged', 'other']", "'rating' should be integer", "unexpected 'mood'"]
The first object passes with an empty error list. The second collects three errors at once: an issue outside the enum, a rating sent as a string, and an unexpected field. Returning all errors together, not just the first, matters when you send them back to the model for a retry.
Limits and Summary
Note: Not every JSON Schema feature is supported by Claude structured outputs. Recursive schemas, numeric limits (minimum, maximum) and string length limits (minLength, maxLength) are not enforced by the API; the Python and TypeScript SDKs remove such constraints from the schema they send and check them on your side instead. Structured outputs also cannot be combined with citations. Keep schemas flat and simple: deep nesting and dozens of optional fields make results worse, not better.
- A JSON Schema describes fields, types, required fields and allowed values.
- Enums are the best way to stop the model inventing categories.
- Schema enforcement guarantees shape; check
stop_reasonfor refusals and truncation. - Rules a schema cannot express still need your own validation code.