Most first prototypes ask Claude a question and show the prose to a person. Most production systems do something else entirely: they take Claude's output and feed it to code — a database insert, a CRM update, a routing rule. Code does not read prose. It needs fields with names and types, every time. "Structured outputs" is the umbrella term for the techniques that turn a language model's answer into machine-readable data, and getting this right is usually the difference between a demo and an integration.
The problem with just asking for JSON
The naive approach is a prompt that ends with "respond in JSON." It works often — and fails in ways that break pipelines: a friendly preamble before the opening brace ("Here is the extracted data:"), markdown code fences around the object, a field named dueDate on Monday and due_date on Tuesday, or a "total" returned as the string "1,204.50" instead of a number. At one request per demo these are anecdotes. At fifty thousand requests a month, a 2% malformed-output rate is a thousand failed records for someone to clean up.
The reliable pattern: a schema, not a plea
The dependable technique on today's platforms uses the tool-use mechanism. You define a "tool" whose input schema is exactly the shape of data you want, and tell Claude it must use it. The model then fills in the schema's fields rather than writing free text — no preamble, no fences, and typed fields. Because tool use is available on all four platforms (Bedrock, Vertex AI, Foundry, and Claude Platform on AWS), this pattern is portable across whichever cloud you run.
from anthropic import AnthropicVertex
client = AnthropicVertex(project_id="my-project", region="global")
schema = {
"type": "object",
"properties": {"vendor": {"type": "string"}, "total": {"type": "number"},
"due_date": {"type": "string", "description": "YYYY-MM-DD"}},
"required": ["vendor", "total", "due_date"],
}
resp = client.messages.create(
model="claude-sonnet-5", max_tokens=1024,
tools=[{"name": "record_invoice", "description": "Record extracted invoice fields.", "input_schema": schema}],
tool_choice={"type": "tool", "name": "record_invoice"},
messages=[{"role": "user", "content": "Extract fields from this invoice:\n" + invoice_text}],
)
The response contains a tool-use block whose input is a JSON object matching your schema — vendor as a string, total as a number, date in the format you described. Your code reads fields instead of parsing prose.
Validate anyway — always
A schema constrains the shape of the output, not the truth of it. Claude can still return a total that does not match the document, a date that fails a sanity check, or a vendor name spelled three ways across a batch. Production pipelines therefore validate every response in code before acting on it: check required fields, ranges, formats, and cross-field consistency (does line-item math sum to the total?). When validation fails, retry once — often with the validation error included in the prompt, which usually fixes it — and if it fails again, route the item to a human queue rather than guessing. This is cheap, deterministic code, and it converts "the model is occasionally wrong" from an outage into a manageable exception rate.
Design tips that pay off
Describe fields, don't just name them. A description like "YYYY-MM-DD" or "one of: low, medium, high" inside the schema does more for consistency than paragraphs of prompt text. For classification fields, use an enum so the model must pick from your list.
Allow an escape hatch. Include a field like confidence or make ambiguous fields nullable, and instruct Claude to use them rather than invent values. Forced guesses are the enemy of clean data.
Keep schemas flat and boring. Deeply nested, cleverly reusable schemas confuse both models and maintainers. One schema per task, shaped like the table it will eventually land in.
Version your schemas. When a field is added or renamed, downstream consumers break exactly like they would with any API change. Treat the schema as a contract under change control.
Where to go next
Structured outputs are one application of tool use, which is worth understanding in full. To see the pattern doing real work, read email and ticket triage — classification is structured output at its purest — or document processing for extraction pipelines.