Claude Code Output Format: JSON & Stream
Use claude -p --output-format with json, stream-json, and text in scripts and CI: one JSON object, NDJSON events, --json-schema, and jq recipes (2026).
Need an API key? Sign up →Endpoints & base URLs →
On This Page
Last verified: 2026-10-20 · 📄 Per official docs (Claude Code CLI reference and headless docs, checked 2026-10-20)
claude -p --output-format json returns a single JSON object once the task finishes; --output-format stream-json returns newline-delimited JSON events while it runs; text is the default when you leave the flag out.
In scripts, CI/CD, and automation pipelines you need more than a human-readable answer — you need structured data a program can parse. Claude Code's headless mode (claude -p) offers three output formats via --output-format, letting you wire Claude Code into a pipeline like any ordinary CLI tool.
This is a Claude Code capability and is independent of which service it connects to: whether Claude Code points at the official API or at QCode,
--output-formatworks the same. This page focuses on output formats; for the full headless flag reference and CI templates, see Automation & CI/CD.
1. The three formats at a glance¶
| Format | Flag | Output shape | Use case |
|---|---|---|---|
| Plain text | --output-format text (default) |
A single block of plain text | Human reading, simple pipes |
| JSON | --output-format json |
One structured JSON object | Script parsing, reading cost/usage/session ID |
| Streaming JSON | --output-format stream-json |
A newline-delimited JSON event stream | Live progress, step-by-step consumption, long tasks |
# text is the default, so these two lines are equivalent
claude -p "Explain idempotence in one sentence"
claude -p "Explain idempotence in one sentence" --output-format text
2. text: the default plain text¶
Without --output-format you get text: stdout is simply the model's final answer — good for reading directly or feeding a simple pipe.
# Read directly
claude -p "Rewrite this function to be async"
# Pipe to another tool
claude -p "Generate 5 candidate commit messages" | head -5
# Write to a file
claude -p "Write a short README intro for this repo" > intro.txt
Limitation: text gives you only the "answer" — no cost, token usage, or session ID metadata, and multi-step tasks can't be distinguished from the final result. When you need that, switch to json or stream-json.
3. json: a single structured object¶
--output-format json emits one JSON object after the task completes, containing the final result plus a batch of metadata fields. This is the format scripts use most.
claude -p "Count how many TODO comments are under src/" --output-format json
Common fields in the returned object (the actual fields depend on your version's output):
| Field | Meaning |
|---|---|
result |
The final answer text |
total_cost_usd |
Cost of this call (USD) |
usage |
Token usage (input / output / cache, etc.) |
session_id |
Session ID, usable with --resume to continue |
Parsing with jq¶
The json format is a natural fit for jq:
# Just the final answer
claude -p "Summarize this change in one sentence" --output-format json | jq -r '.result'
# Pull out the cost of this call
claude -p "Review auth.py" --output-format json | jq '.total_cost_usd'
# Grab several fields at once
claude -p "Analyze the architecture" --output-format json \
| jq '{cost: .total_cost_usd, session: .session_id, tokens: .usage}'
Branching and accumulating cost in a script¶
#!/usr/bin/env bash
set -euo pipefail
out=$(claude -p "Write unit tests for UserService" \
--output-format json --max-turns 8)
result=$(echo "$out" | jq -r '.result')
cost=$(echo "$out" | jq -r '.total_cost_usd')
sid=$(echo "$out" | jq -r '.session_id')
echo "Result: $result"
echo "Cost this call: \$$cost"
echo "Session ID: $sid (resume with claude --resume $sid)"
Structured output with --json-schema¶
Add --json-schema and a JSON Schema definition to --output-format json, and the response carries the structured result in the structured_output field, alongside metadata about the request (session ID, usage, and so on). The example below extracts function names as an array of strings and keeps only the structured part with jq:
claude -p "Extract function names from auth.py" \
--output-format json \
--json-schema '{"type":"object","properties":{"functions":{"type":"array","items":{"type":"string"}}},"required":["functions"]}' \
| jq '.structured_output'
Remove the | jq '.structured_output' part to print the whole response instead. If the value isn't a valid JSON Schema, claude exits with Error: --json-schema is not a valid JSON Schema, followed by the validator's diagnostic.
Tip:
jsonemits the whole object in one shot after the task ends — there is no output while a long task runs. To watch progress live, usestream-jsonbelow.
4. stream-json: a real-time event stream¶
--output-format stream-json breaks execution into newline-delimited JSON events (NDJSON), one event per line, written out immediately. It suits live progress for long tasks, streaming UIs, or pipelines that consume output as it's produced.
claude -p "Refactor the whole utils directory and add tests" \
--output-format stream-json --max-turns 20
The output looks like (each line is independent JSON; fields depend on your version):
{"type":"system","subtype":"init","session_id":"..."}
{"type":"assistant","message":{...}}
{"type":"assistant","message":{...}}
{"type":"result","result":"...","total_cost_usd":0.0123,"usage":{...}}
Consuming line by line¶
Every line is valid JSON, so you can process it as it streams:
# Print the type of each event in real time
claude -p "Migrate to the new API" --output-format stream-json \
| jq -r '.type'
# Pull result and cost only when the final event appears
claude -p "Migrate to the new API" --output-format stream-json \
| jq -r 'select(.type == "result") | "\(.result)\nCost $\(.total_cost_usd)"'
# Process line by line with a while loop for live progress feedback
claude -p "Repository-wide code review" --output-format stream-json | \
while IFS= read -r line; do
type=$(echo "$line" | jq -r '.type')
case "$type" in
system) echo "▶ Starting…" ;;
assistant) echo "… thinking" ;;
result) echo "✓ Done: $(echo "$line" | jq -r '.result')" ;;
esac
done
Token-level and subagent events¶
--include-partial-messages: with--output-format stream-json --verbose, receive tokens as they are generated; each line is a JSON object representing an event.--include-hook-events: include hook lifecycle events in the output stream (requires--output-format stream-json).--forward-subagent-text: emit subagent text into the stream-json output stream (requires--printand--output-format stream-json; Claude Code v2.1.211 or later). Without it, a foreground subagent's text is omitted.--input-format stream-json: take the input in print mode as a JSON stream instead of a text prompt.
jsonvsstream-json:jsonwaits for the task to finish and gives you one complete object — simple to parse;stream-jsonstreams multiple events throughout, enabling live feedback but requiring line-by-line parsing. In CI, usejsonfor the final result andstream-jsonfor progress logs.
5. CI / pipeline recipes¶
Grab the result and post a comment in GitHub Actions¶
- name: Claude review
env:
ANTHROPIC_BASE_URL: https://api.qcode.cc/api
ANTHROPIC_AUTH_TOKEN: ${{ secrets.QCODE_API_KEY }}
run: |
out=$(claude -p "Review this PR's diff and list issues" \
--output-format json --max-turns 6 --allowedTools Read,Glob,Grep)
echo "$out" | jq -r '.result' > review.md
echo "This review cost \$$(echo "$out" | jq -r '.total_cost_usd')"
Add a cost gate (fail if over budget)¶
out=$(claude -p "Generate release notes" --output-format json)
cost=$(echo "$out" | jq -r '.total_cost_usd')
# Float comparison with awk: fail CI if over $0.50
awk -v c="$cost" 'BEGIN{ exit (c > 0.5) ? 1 : 0 }' \
|| { echo "Cost \$$cost over budget"; exit 1; }
echo "$out" | jq -r '.result'
Accumulate cost across multiple calls¶
total=0
for f in src/*.py; do
out=$(claude -p "Write docstrings for $f" --output-format json)
c=$(echo "$out" | jq -r '.total_cost_usd')
total=$(awk -v t="$total" -v c="$c" 'BEGIN{ printf "%.4f", t + c }')
done
echo "Total cost across all files: \$$total"
Pointing at QCode: the commands above are identical for the official API and QCode — just point
ANTHROPIC_BASE_URLathttps://api.qcode.cc/api(note: no trailing slash) and put yourcr_key inANTHROPIC_AUTH_TOKEN. CN users can useasia.qcode.cc. See Endpoints & API formats.
6. Common pitfalls¶
jsonis silent during long tasks: it outputs once, at the end — seeing no progress is normal; usestream-jsonfor progress.- You can't
jqstream-jsonas one blob: it's NDJSON; multiple objects don't form a single JSON document. Use line-by-linejq,jq -c, or--stream— don't runjq '.'over the whole stream. - Don't forget
--max-turns: cap turns in automation to keep a task from running away and burning money. - No trailing slash on BASE_URL:
https://api.qcode.cc/apiis correct; an extra/builds a wrong path. - Metadata fields vary by version: trust the real output of
claude -p ... --output-format jsonon your machine — runjq 'keys'first to see what's there.
Next steps¶
- Automation & CI/CD — full headless flag reference and CI/CD recipes
- Subagents — have Claude Code orchestrate background agents in parallel
- Cost optimization — manage cost with
total_cost_usd - Endpoints & API formats — the three domains and protocol paths
Once Claude Code is wired into your pipeline, cost is right there in the output — see QCode plans & pricing and pick a tier that fits your automation volume.
Get started with QCode
One API key for Claude Code, Codex and more — billed per token, per-model prices on qcode.cc/models