Claude Certification Guides
CCAR-F/Domain 49 min read

Lesson 4.4

Structured Output: JSON Mode, Tools and Schemas

The three ways to get machine-readable output from Claude, what each guarantees, and how to design schemas that work.

Applications need output they can parse. Historically that meant asking nicely for JSON and writing defensive code around the result. The API now offers guaranteed schema conformance through structured outputs, alongside strict tool use for tool arguments. The exam tests which mechanism gives which guarantee, the schema rules, and the failure modes that remain even with guarantees.

Three approaches

ApproachGuaranteeTypical use
Prompt for JSON ("respond only with JSON matching...")None; usually works, sometimes wrapped in prose or code fencesPrototypes, models without structured outputs
Tool use as an extractor (one tool whose schema is your output)Schema-valid arguments when strict: true; historically forced with tool_choiceLegacy pattern; forced tool_choice is rejected by newer models
Structured outputs (output_config.format)The response text is valid JSON matching your schemaAny production extraction or classification

Only the third turns "parse and hope" into "parse". Use it whenever the model supports it.

Structured outputs

schema = {
    "type": "object",
    "properties": {
        "sentiment": {"type": "string", "enum": ["positive", "neutral", "negative"]},
        "topics": {"type": "array", "items": {"type": "string"}},
        "needs_followup": {"type": "boolean"},
    },
    "required": ["sentiment", "topics", "needs_followup"],
    "additionalProperties": False,
}

response = client.messages.create(
    model=MODEL,
    max_tokens=1024,
    output_config={"format": {"type": "json_schema", "schema": schema}},
    messages=[{"role": "user", "content": review_text}],
)
data = json.loads(response.content[0].text)

The older top-level output_format parameter is deprecated in favour of output_config.format. The SDKs add a parse() helper (client.messages.parse in Python, with a Pydantic model; the TypeScript equivalent with Zod) that generates the schema from your type, sends the request and returns a typed object, which removes the hand-written schema and the json.loads call.

Schema rules

Structured outputs support a subset of JSON Schema and apply some rules of their own:

  • Objects must set additionalProperties: false and list every property in required (optional fields are expressed as a union with null).
  • Supported keywords include type, enum, properties, items, anyOf, const and $ref with definitions; many value constraints (numeric ranges, string length, format) are not enforced by the grammar and should be validated by you or rewritten as descriptions.
  • Keep schemas as small as the task allows. Deep nesting and many optional branches make generation slower and less accurate.
  • The first request with a new schema pays a compilation cost; subsequent requests with the same schema reuse it.

Check the structured outputs page in the documentation for the current list of supported keywords and limits.

Strict tool use

The same guarantee applies to tool arguments: set strict: true on a tool definition and tool_use.input will validate against input_schema, subject to the same schema rules. This is the right mechanism when the structured data is an action (call this function with these arguments) rather than an answer. Both mechanisms can be active in one request: strict tools for the calls and output_config.format for the final response.

What guarantees do not cover

Schema conformance says nothing about truth. A valid {"invoice_total": 0} is still wrong if the invoice said 1,250. Keep semantic validation (ranges, cross-field consistency, reconciliation against source data) in your code. Three other cases break parsing even with structured outputs:

  • stop_reason: "max_tokens": the JSON was cut off. Raise max_tokens and retry.
  • stop_reason: "refusal": the response is a refusal, not JSON. Check stop_reason before parsing.
  • Incompatible features: structured outputs cannot be combined with citations, and a request mixing them returns a 400.

Prompt design still matters

The schema constrains shape; the prompt still determines content. Describe each field's meaning in the prompt or in the schema's description properties, give examples of difficult inputs, and state how to handle missing information (the schema can make a field nullable, but the prompt tells the model when to use null). Enumerations should be explained, not just listed.

Choosing a design

  • Classification or extraction into a known shape: structured outputs, with enums for closed sets.
  • The model should decide between several actions: strict tools, one per action.
  • Free-text answer with a small structured envelope (answer plus confidence plus sources): structured outputs with a text field; or XML tags and a stop sequence when the model lacks structured outputs.
  • Multiple records from a long document: a schema with an array, but watch max_tokens; chunk the input if records are numerous.

Exam focus

  • Prompt-only JSON has no guarantee; strict tools guarantee valid arguments; structured outputs (output_config.format with json_schema) guarantee the response matches the schema.
  • Forcing a tool call via tool_choice to obtain JSON is a legacy pattern that newer models reject; use structured outputs.
  • Schemas need additionalProperties: false and all properties in required; optional means nullable.
  • messages.parse() builds the schema from a Pydantic or Zod type and returns a typed object.
  • Schema conformance does not guarantee correctness; semantic validation stays in your code.
  • Parsing still fails on max_tokens truncation and refusal; structured outputs are incompatible with citations.

Check yourself

5 questions

  1. 1.A service needs Claude's response to be valid JSON that always matches a given schema, without post-hoc repair code. Which approach provides that guarantee?

  2. 2.Which two features make up Anthropic's structured outputs capability?

  3. 3.A structured-outputs request fails because the schema uses numeric minimum and maximum constraints and a recursive $ref. Why?

  4. 4.A request that combines output_config.format with document blocks that have citations enabled returns a 400 error. Why?

  5. 5.A structured-outputs response comes back with stop_reason max_tokens. How should the application treat the output?