Schema-validated LLM output with claude --json-schema
Pipe HN/anything into claude with a strict JSON Schema — server-side validates the output shape. Downstream jq can trust every field.
Setup
- → claude /login OR export ANTHROPIC_API_KEY=sk-…
Cost per run
<$0.01
The one-liner
$ curl -s \
"https://hn.algolia.com/api/v1/search?query=local+llm&tags=story&hitsPerPage=20" \
| jq -r '.hits[] | "- [\(.points // 0)] \(.title)"' \
| claude -p \
--output-format json \
--json-schema '{
"type":"object",
"required":["theme","sentiment","top_post"],
"properties":{
"theme":{"type":"string"},
"sentiment":{"type":"string","enum":["bullish","bearish","mixed"]},
"top_post":{"type":"string"}
}
}' \
"Summarize these posts: theme (one phrase), sentiment, top_post." \
| jq -r '.result'What each stage does
- [01] curl
curl … hn.algolia.com/api/v1/search?query=local+llm&tags=story …HN Algolia search by keyword. No auth, no User-Agent tricks (Reddit's free JSON API now 403s every unauthenticated call). - [02] jq
jq -r '.hits[] | "- [\(.points // 0)] \(.title)"'Tight prompt input — one '[points] title' line per story. // 0 defaults null scores to 0. - [03] claude
claude -p --output-format jsonReturns a JSON envelope with .result, .session_id, .duration_ms, .usage. Pair with --json-schema or it's just stringified text inside. - [04] claude
--json-schema '{ "type":"object", … }'Server-side validation. Claude's response is constrained to match — invalid shapes fail at the API, not in your shell. Downstream jq can trust every field exists. - [05] jq
jq -r '.result'Extract the validated payload from the envelope. .result is now guaranteed to match your schema.
Expected output (sample)
{
"theme": "local-first LLM tooling and quantization tricks",
"sentiment": "bullish",
"top_post": "Running Llama 4 70B on a $400 mini-PC at 8 tok/s"
}Caveats & tips
- Both --output-format json AND --json-schema are required together. Without --output-format, the schema is ignored.
- Schema enforcement is strict — extra fields cause an error. Use `additionalProperties: false` defensively.
- Pipe straight into a database INSERT, a webhook, or a typed config — no defensive parsing.