Prompt Engineering & Output Quality

Extraction to JSON via Prompt-Only Techniques

You don't always need schema enforcement to get parseable JSON out of Claude. A well-built prompt gets you most of the way — and knowing its limits tells you exactly when to upgrade.

Claude 3P 101 · Updated July 2026 · Unofficial guide

Pulling structured fields out of unstructured text — invoices, emails, contracts — is one of the most common enterprise uses of Claude, and the output usually needs to be JSON so code can consume it. The API offers hard guarantees for this (structured outputs, strict tool use), but prompt-only JSON remains worth mastering: it works on every platform and every model with zero configuration, it has no schema-feature restrictions, and it is often how a pipeline starts before graduating to enforcement. These patterns produce reliably parseable output; they just don't guarantee it.

The four instructions that do the work

Reliable prompt-only JSON comes from stacking four explicit instructions — Anthropic's guidance to be clear and direct, applied ruthlessly:

Extract the following from the invoice into JSON with exactly
these keys:
{"vendor": string, "invoice_number": string,
 "total": number, "currency": string,
 "line_items": [{"description": string, "amount": number}]}
Rules:
- Return ONLY the JSON object. No preamble, no explanation,
  no markdown code fences.
- If a field is not present in the document, use null. Never
  invent a value.
- Output must be valid, parseable JSON: double quotes, no
  trailing commas, no comments.

The shape example matters more than prose descriptions of the shape — showing the exact key set and types anchors the output far better than "return the vendor, number, and total as JSON." The null rule matters because extraction prompts without a missing-value policy hallucinate one, and the "only the JSON" rule eliminates the preamble and code fences that break naive json.loads() calls.

Add examples for the ugly inputs

For consistency across varied documents, add few-shot examples — Anthropic recommends 3–5 relevant, diverse examples wrapped in <example> tags. Diversity is the point: include one clean invoice, one with a missing field (showing null in the output), and one with an oddity like multiple currencies. Wrap the document itself in an XML tag such as <invoice_text> so the input never blurs into the instructions. A note on an obsolete trick: prefilling the assistant turn with { to force JSON mode is no longer supported from the Claude 4.6 generation onward — it returns a 400 error. The documented replacements are exactly the direct instructions above, or structured outputs.

import json
from anthropic import AnthropicBedrockMantle

client = AnthropicBedrockMantle(aws_region="us-east-1")
msg = client.messages.create(
    model="anthropic.claude-sonnet-5",
    max_tokens=1024,
    system=EXTRACTION_PROMPT,   # the instruction block above
    messages=[{"role": "user",
               "content": f"{doc}"}],
)
data = json.loads(msg.content[0].text)  # validate before use

Parse, validate, retry — in code

Prompt-only means the last line of defense is yours. Production pipelines wrap the call in a short deterministic loop: parse the response; if parsing fails, retry once with the parser's error message appended ("your previous output was not valid JSON: expecting ',' at line 4 — return corrected JSON only"); if it fails again, route to a dead-letter queue rather than looping forever. Validate semantics too, not just syntax — required keys present, totals numeric, enums in range — with an ordinary schema validator on your side. This loop is cheap, and it converts "reliably parseable" into "operationally dependable" without any API features at all.

Rule of thumb: prompt-only JSON plus a parse-validate-retry loop is fine for internal pipelines with a dead-letter path. The moment malformed output has no acceptable failure mode, switch to structured outputs.

Know when to graduate to enforcement

The API's structured outputs feature — output_config: {"format": {"type": "json_schema", "schema": {…}}} — makes the model's output conform to your JSON schema at generation time, and it is generally available on the Claude API and supported on Claude Platform on AWS, Vertex AI, Bedrock (a subset), and Microsoft Foundry. The SDKs even ship helpers (Python's client.messages.parse() with Pydantic models) that validate client-side too. So why ever stay prompt-only? Three honest reasons. Schema restrictions: structured outputs reject certain JSON Schema features — recursive schemas, numeric minimum/maximum, string length constraints among them — that a prompt handles without complaint. Feature conflicts: citations cannot be combined with structured outputs in a single request. And even with enforcement, correctness isn't guaranteed — a refusal returns stop_reason: "refusal" and truncation (max_tokens) can yield invalid output — so the validation habit stays either way.

Where to go next

The enforcement path is covered in structured outputs; broader extraction craft in prompt patterns for information extraction. For shaping non-JSON output, see controlling output format.

Sources