Structured JSON Output
When your app needs to parse the reply, ask Claude for JSON in a defined shape.
When Claude's output feeds back into your code, you need it in a predictable structure — usually JSON. Claude is good at producing valid JSON when you describe the exact shape.
How to get reliable JSON
- Describe every field and its type.
- Give a small example of the exact structure.
- Ask for JSON and nothing else — no prose around it.
Parse defensively
Even with clear instructions, wrap parsing in a try/catch. If Claude ever adds stray text, you can detect it and retry rather than crash.
Example
const res = await client.messages.create({
model: "claude-opus-4-8", max_tokens: 300,
messages: [{
role: "user",
content:
'Extract contact info as JSON with keys "name", "email", "company". ' +
'Return only JSON.\n\n' +
'Text: Jane Doe from Acme wrote in from [email protected] about pricing.',
}],
});
const raw = res.content.find((b) => b.type === "text")?.text ?? "{}";
try {
const data = JSON.parse(raw);
console.log(data.email); // [email protected]
} catch {
console.error("Model did not return valid JSON — retry or repair.");
}When to use it
- Parse product details from unstructured supplier emails into a typed object your database insert can consume directly.
- Extract action items and assignees from a meeting transcript into a JSON array for your task-management app.
- Classify user-submitted feedback into category, sentiment, and priority fields for a structured support queue.
More examples
Ask for JSON with a defined schema
Uses a system rule to ban prose and a schema hint in the user message to constrain the JSON shape.
const msg = await client.messages.create({
model: "claude-haiku-4-5",
max_tokens: 128,
system: "Respond only with valid JSON. No explanation, no code blocks.",
messages: [{
role: "user",
content: `Extract from: "Jane Smith, [email protected], Senior Engineer".
Schema: {"name":string,"email":string,"role":string}`
}],
});
const data = JSON.parse(msg.content[0].text);Use a tool definition for reliable JSON
Forces tool_choice to a specific tool so the output is always a validated JS object, bypassing JSON.parse.
const msg = await client.messages.create({
model: "claude-haiku-4-5",
max_tokens: 256,
tools: [{
name: "extract_contact",
description: "Extract contact info from text.",
input_schema: {
type: "object",
properties: {
name: { type: "string" },
email: { type: "string", format: "email" },
role: { type: "string" }
},
required: ["name", "email"]
}
}],
tool_choice: { type: "tool", name: "extract_contact" },
messages: [{ role: "user", content: "Jane Smith, [email protected], Senior Engineer" }],
});
const data = msg.content[0].input; // already an object — no JSON.parse neededWrap parse in try/catch for safety
Strips code-fence wrappers Claude sometimes adds and throws a descriptive error if parsing still fails.
function parseClaudeJson(text) {
// Strip accidental code fences
const clean = text.replace(/^```json\n?/, "").replace(/\n?```$/, "").trim();
try {
return JSON.parse(clean);
} catch {
throw new Error(`Claude returned invalid JSON: ${clean.slice(0, 80)}`);
}
}
Discussion