Output formats: json / text / stream-json
Use claude -p's --output-format to handle Claude Code output in scripts and CI — structured json parsing, real-time stream-json event streams, default plain text, with jq and pipe recipes
Output formats: json / text / stream-json¶
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 the upstream: 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)"
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
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 four 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.