Make Claude return schema-valid JSON: use tool use instead of “please return JSON”
TL;DR
Declare a tool with an input_schema (JSON Schema), set tool_choice to force that tool, then read the input of the tool_use block. Validate server-side and retry on failure. The Claude API also has an official structured outputs feature — check the docs to pick the right approach.
Example: ticket extraction
tools = [{
"name": "save_ticket",
"description": "Save a classified ticket",
"input_schema": {
"type": "object",
"properties": {
"category": {"type": "string", "enum": ["bug", "billing", "feature", "other"]},
"priority": {"type": "string", "enum": ["P1", "P2", "P3"]},
"summary": {"type": "string"}
},
"required": ["category", "priority", "summary"]
}
}]
msg = client.messages.create(
model="claude-sonnet-5-5", max_tokens=500, tools=tools,
tool_choice={"type": "tool", "name": "save_ticket"},
messages=[{"role": "user", "content": ticket_text}])
data = next(b.input for b in msg.content if b.type == "tool_use")Why it beats prompting
- enum constrains values — no more “High”, “high”, “urgent” mixed together.
- required enforces every field.
- No “Here is your JSON:” preamble breaking your parser.
Production checklist
- Re-validate with Pydantic / Zod on the server.
- Retry 1–2 times with the validation error.
- Log inputs/outputs for debugging.
- Use a small model for simple extraction, plus the Batch API for bulk runs.