You install Claude Code, type claude, and get an interactive chat. Useful — but not scriptable. The moment you need Claude in a shell script, a CI pipeline, or a cron job, interactive mode is useless. You need headless mode.
The claude -p flag (short for --print) runs Claude headless — no interactive UI, just a prompt in and a response out. This is the foundation for every script, pipeline, and automation you will build.
Text Output Mode
The simplest way to call Claude from a terminal:
$ claude -p "What is the capital of France?"Paris is the capital of France.No metadata, no JSON — just the result field as plain text. This is the default output format when you use -p, and it is perfect for quick questions or piping a response directly into another command.
But for automation, you need more than the answer. You need the session ID, token counts, cost, and stop reason. That is where structured output comes in.
JSON Output Mode
Add --output-format json to get the full response envelope:
$ claude -p "Say exactly: Hello CLI Test" --output-format jsonEvery JSON response wraps the actual answer inside a typed envelope. The result field contains the same text you would see in text mode, but now it comes alongside metadata you can parse, log, and act on.
The JSON Envelope
Top-Level Fields
| Field | Type | Description |
|---|---|---|
type | string | Always “result” for a completed response |
subtype | string | ”success” or “error_max_budget_usd” if the cost limit was hit |
is_error | boolean | false on success — check this first in every response handler |
duration_ms | number | Wall-clock time including tool execution |
result | string | The text response, identical to what text mode returns |
stop_reason | string | ”end_turn” when Claude finished naturally, “max_tokens” if truncated |
session_id | string | UUID for this conversation — pass to —resume to continue it later |
total_cost_usd | number | Cost in USD for this single call |
usage | object | Token breakdown: input_tokens, cache_creation_input_tokens, cache_read_input_tokens, output_tokens |
permission_denials | array | Tools that were blocked by the permission system — empty when nothing was denied |
The total_cost_usd field is the cost for this single call, not a cumulative total. In a resumed session, each response reports only its own cost. If you need to track spend across an entire conversation, sum total_cost_usd from every call in the chain.
Pipe JSON output to jq for quick extraction on the command line:
claude -p ”…” —output-format json | jq ‘.result’
Run your first JSON call and extract the cost:
claude -p “What is 2+2?” —output-format json | jq ‘{answer: .result, cost: .total_cost_usd, session: .session_id}‘
Save that session_id — you’ll use it in the Sessions chapter to resume this conversation.
Stream-JSON Preview
When you need real-time output — for a typewriter UI, a progress indicator, or a long-running agent pipeline — there is a third format: stream-json.
$ claude -p "Say exactly: Hello CLI Test" --output-format stream-json --verboseThis emits NDJSON (newline-delimited JSON) events in real time, one JSON object per line. Each line has a type field that tells you what kind of event it is: system for initialization metadata, assistant for text responses, rate_limit_event for API status, and result for the final envelope.
The stream protocol is covered in depth in the Stream Protocol chapter, including how to parse partial messages for typewriter effects and how to handle tool-use events mid-stream.
Run claude -p “Hello” —output-format json | jq ‘{cost: .total_cost_usd, session: .session_id}’. You now know the cost and session ID for every call. Try piping a different question and compare costs — even trivial prompts have a minimum cost from the system prompt cache.