Structured outputs

Prompt Engineering

Chapter 7  ·  Structured Outputs — JSON, Tables, HTML, and Specific Formats on Demand

One of the most practical prompt engineering skills is getting Claude to produce output in an exact format — JSON you can parse, a markdown table you can paste into a doc, a CSV ready to import, HTML you can drop into a page. Claude can do all of these reliably, but the prompting matters. This chapter covers the patterns that work and the failure modes to watch for.

The Golden Rule: Specify Format Last

As established in Chapter 1, the end of your message carries the most weight. Format instructions belong at the end of your prompt — after the task, after the context, after the constraints. If you put them at the beginning, Claude may drift by the time it gets to generating the output.

Format instruction placement — matters more than you'd expect
Weak
Return only valid JSON. List three Linux commands for monitoring disk usage with a brief description of each.
Better
List three Linux commands for monitoring disk usage with a brief description of each. Return only valid JSON — no explanation, no markdown fences, just the raw JSON object.

Getting Clean JSON

JSON is the most commonly requested structured format — it's the lingua franca of pipelines, scripts, and APIs. The challenge is that Claude defaults to wrapping code in markdown fences and adding explanatory prose, which breaks a JSON.parse() call.

The reliable JSON prompt pattern

Extracting structured data as JSON
Prompt
Extract the following fields from this job posting and return them as a JSON object. Fields: title, company, location, salary_range (null if not mentioned), remote (true/false), required_skills (array of strings). Job posting: "Senior Python Developer at FinStack Ltd. London-based with hybrid working (3 days in office). Competitive salary £70k–£90k. Must have: Python 3.10+, FastAPI, PostgreSQL, Docker. Nice to have: Kubernetes, AWS." Return only the JSON object. No explanation, no markdown code fences.
Result
{ "title": "Senior Python Developer", "company": "FinStack Ltd", "location": "London", "salary_range": "£70k–£90k", "remote": false, "required_skills": ["Python 3.10+", "FastAPI", "PostgreSQL", "Docker"] }

Three things make this work:

  • The schema is defined upfront — field names, types, and null behaviour are all specified
  • "No markdown code fences" — explicitly prevents the ```json wrapper Claude adds by default
  • Format instruction is last — "Return only the JSON object. No explanation." closes the prompt
If you're piping to code
Add Ensure all strings use double quotes and the output is valid JSON that passes JSON.parse(). This prevents Claude from using single quotes or trailing commas that some parsers reject.

Defining the schema with an example

For complex or nested JSON, show Claude an example of the structure you want (few-shot from Chapter 4 applies here):

Schema definition via example
Return results in this exact JSON structure:
{
  "commands": [
    {
      "name": "string",
      "syntax": "string",
      "description": "string",
      "example": "string"
    }
  ]
}
No extra fields. No explanation. No markdown fences.

Markdown Tables

Markdown tables render cleanly in most documentation tools, GitHub, and note-taking apps. They're also easy for Claude to produce consistently.

Requesting a markdown table with specific columns
Prompt
Compare WireGuard, OpenVPN, and IKEv2 across these dimensions: setup complexity, speed, protocol (UDP/TCP), codebase size, and best use case. Format as a markdown table with those exact column headers. One row per VPN.
Result
| VPN | Setup Complexity | Speed | Protocol | Codebase Size | Best Use Case | |-----------|-----------------|--------|----------|---------------|---------------------------| | WireGuard | Low | Fast | UDP only | ~4,000 lines | Self-hosted, mobile, speed | | OpenVPN | High | Medium | UDP/TCP | ~70,000 lines | Firewall bypass (TCP 443) | | IKEv2 | Medium | Fast | UDP | Varies | Mobile roaming (MOBIKE) |
Column header consistency
If you need to paste the table into a script that parses column headers, name the columns exactly as you want them — Claude will match your names precisely. Vague column descriptions result in Claude choosing its own labels.

CSV Output

CSV is slightly trickier because commas in values need quoting, and Claude may add a header row you don't want (or omit one you do). Be explicit:

CSV with explicit requirements
Prompt
Convert this list of servers to CSV. Columns: hostname, ip_address, role, os. Include a header row. Quote any field that contains a comma. No extra text. Servers: - web-01, 192.168.1.10, web server, Ubuntu 22.04 - db-01, 192.168.1.20, database, Debian 12 - cache-01, 192.168.1.30, Redis cache, Ubuntu 22.04
Result
hostname,ip_address,role,os web-01,192.168.1.10,web server,Ubuntu 22.04 db-01,192.168.1.20,database,Debian 12 cache-01,192.168.1.30,Redis cache,Ubuntu 22.04

HTML Fragments

For web projects, asking Claude to produce ready-to-use HTML saves a translation step. The key is specifying whether you want a full document or a fragment, and what CSS classes or structure to use.

HTML fragment with class and structure requirements
Prompt
Write an HTML fragment (no DOCTYPE, no html/head/body tags) for a "server status" card. Use a div with class "status-card". Inside: an h3 with the server name, a span with class "status-badge" showing the status, and a p with the IP address. Use placeholder values. No inline styles — classes only.
Result
<div class="status-card"> <h3>web-01</h3> <span class="status-badge">Online</span> <p>192.168.1.10</p> </div>

Format Recipe Cards

Here are reliable prompt patterns for the most common structured output types:

JSON object
Return only a valid JSON object with these fields: [list]. No explanation, no markdown fences.

Add "Ensure all strings use double quotes" if parsing programmatically.

JSON array
Return a JSON array of objects. Each object has: [fields]. No wrapper object, no explanation, no fences.

Specify whether the array should be wrapped in a root key or bare.

Markdown table
Format as a markdown table with these exact column headers: [list]. One row per [item].

Name columns exactly as you want them to appear.

CSV
Output as CSV with a header row. Columns: [list]. Quote fields containing commas. No extra text before or after.

Specify delimiter if not comma (e.g. semicolon for European locales).

Numbered list
Return a numbered list only. No introduction, no conclusion, no sub-bullets. Each item one sentence.

Add "exactly N items" to enforce a fixed count.

Key: value pairs
Return the result as key: value pairs, one per line. Keys: [list]. No other formatting.

Useful for config-style output or quick reference cards.

Common Failure Modes

FailureWhy it happensFix
Markdown fences around JSON Claude's default is to wrap code in ```json blocks for readability Explicitly add "no markdown fences" or "raw JSON only" to the prompt
Explanation before/after the output Claude wants to be helpful by contextualising its output "No explanation. Output only." at the end of the prompt — repetition is fine
Trailing commas in JSON Claude sometimes produces JS-style JSON which some strict parsers reject "Valid JSON that passes JSON.parse()" — this phrasing triggers standards compliance
Wrong number of table columns Claude added or merged columns based on what seemed logical Name columns explicitly and add "use exactly these column headers"
Format drift over a long conversation Format instructions from earlier turns lose weight as the conversation grows Restate the format requirement in each message where it matters
Null vs. missing key in JSON Claude may omit a key entirely rather than setting it to null Specify: "Include all fields even if the value is null — never omit a key"

Structured Output in Pipelines

When Claude's output feeds directly into code, a few extra practices help:

  • Parse defensively. Even with good prompting, occasionally Claude adds a preamble or trailing sentence. Strip whitespace and, for JSON, try parsing after removing any ``` fences as a fallback.
  • Validate the schema. Use a library like Python's pydantic or JavaScript's zod to validate Claude's output against your expected shape before passing it downstream.
  • Use the API's structured output feature. When calling Claude via the API, you can pass a JSON schema directly as a parameter — this enforces structure at the model level rather than relying purely on prompting. Covered in the Advanced API course.
Quick sanity check
If you're building a pipeline that depends on Claude's output format, test it with a dozen varied inputs before going to production. Format prompts that work 95% of the time will fail 5% of the time — your code should handle that gracefully.
Next — Chapter 8: Handling Ambiguity
When Claude guesses vs. when it should ask — and how to write prompts that get the right behaviour for each situation. Covers how to invite clarifying questions, how to prevent unnecessary hedging, and how to signal when you want Claude to make a reasonable assumption and proceed.