Claude Code Headless Mode: --print Flag and CI Use in 2026
Last updated: April 15, 2026
Headless Mode in Claude Code
Headless mode runs Claude Code as a one-shot command that prints its result to stdout and exits. No REPL, no approval prompts, no session memory between invocations. You pass a prompt, it runs the task, and the output lands on stdout ready to be piped into another process. This is the form you use from shell scripts, CI pipelines, cron jobs, and any automation that cannot sit at a keyboard waiting for input.
The --print flag
The trigger is the --print flag, often aliased as -p. A minimal example:
claude --print "What does the package.json in this directory expose as entry points?"
That single line spawns a Claude session, reads package.json, generates an answer, writes it to stdout, and exits with code 0. No prompt, no approval step, no REPL.
You can pipe the output the same way you would pipe any Unix tool:
claude -p "summarize the last commit in one sentence" | tee /tmp/summary.txt
When headless mode is the right choice
Use it when the input is known up front and the output is structured enough to consume programmatically. Typical cases:
- Shell scripts that call Claude once and parse the result
- CI jobs on GitHub Actions, GitLab CI, CircleCI, or Jenkins
- Cron-driven nightly reports like changelog drafts or dependency audits
- One-shot file transformations where a prompt describes the edit
- Background tasks kicked off by a webhook or a queue worker
Avoid it when the task needs iteration, clarification, or tool approvals from a human. For those cases interactive mode earns its keep.
Command syntax and flags
The full invocation accepts several flags that matter in automation:
claude --print \
--model sonnet \
--max-turns 8 \
--dangerously-skip-permissions \
--output-format json \
"your prompt here"
Key flags:
--model sonnet | opus | haiku- picks the model. Defaults to whatever is set in~/.claude/settings.json.--max-turns N- upper bound on tool-call rounds. Caps runaway loops.--dangerously-skip-permissions- skips the approval UI for writes and shell commands. Required for fully automated runs. Only set this inside a sandboxed CI job or a container you control.--output-format text | json- plain text prose by default, or structured JSON with turns, tool calls, and token counts.--session- persists context across calls for the rare case you want a two-step headless workflow.
Authentication in automation
Headless runs need an API key in the environment. OAuth via claude.ai does not work in a non-interactive context because it expects a browser.
export ANTHROPIC_API_KEY=sk-ant-api03-...
claude -p "review the diff and flag any regressions" < /tmp/diff.patch
In CI, store the key as an encrypted secret (GitHub Actions secrets, GitLab masked variables, Vercel env vars). Never print it to logs, and never commit it to a config file.
For team usage, rotate keys quarterly and scope them per workload. A key that only does code review does not need write access to unrelated Anthropic resources.
Cost tracking without /cost
The interactive /cost slash command does not exist in headless mode because there is no session. To track spend, ask for JSON output and parse the token counts.
claude -p "audit the API routes for auth gaps" \
--output-format json > /tmp/result.json
jq '.usage' /tmp/result.json
The usage object includes input_tokens, output_tokens, and cache_read_input_tokens. Multiply by your current per-token rate to convert to dollars. At Sonnet 4 rates of about 3 dollars per million input tokens and 15 dollars per million output tokens, a nightly run that processes 40k input and 2k output tokens lands near 15 cents.
For longer-term visibility, append usage records to a CSV and chart the result weekly. This beats guessing at month-end when the Anthropic bill arrives.
Three concrete examples
1. Code review script
#!/usr/bin/env bash
set -euo pipefail
git diff origin/main...HEAD > /tmp/diff.patch
claude -p "Review this diff. Flag bugs, missing tests, and security issues. Be terse." < /tmp/diff.patch
Wire this into a pre-push hook or a PR check. The output goes straight into the terminal or the CI log.
2. Changelog generator
git log v1.4.0..HEAD --oneline | \
claude -p "Rewrite these commits as a user-facing changelog in Markdown. Group by feature, fix, chore."
The result lands on stdout and can be pasted into CHANGELOG.md or emailed to the team.
3. File transformer
for f in docs/*.mdx; do
claude -p "Rewrite this Markdown for clarity. Keep headings. Output only the rewritten document." < "$f" > "$f.new"
mv "$f.new" "$f"
done
One pass across the docs directory rewrites every file. Run it in a branch, review the diff, merge or discard.
Differences from interactive mode
Several features that exist in the REPL are absent in headless mode:
- No slash commands.
/compact,/model,/statusall fail because there is no active session. - No session memory. Each invocation starts from scratch unless you pass
--session. - No tool approval UI. Writes and shell commands either run with
--dangerously-skip-permissionsor are blocked. - No progress spinner or token counter. The tty is assumed to be captured by another process.
- No auto-compaction. If the task blows past the context window, the call fails rather than summarizing itself.
Plan for these gaps when porting an interactive workflow to automation.
Timeouts and error handling
Claude Code exits with code 0 on success and non-zero on failure. A partial list:
- 0: task completed
- 1: generic error, check stderr
- 2: invalid flags or missing prompt
- 3: authentication failed
- 124: hit the
--max-turnslimit - 130: killed by SIGINT
Wrap the call with timeout to enforce a wall-clock budget:
timeout 300 claude -p "$PROMPT" || {
echo "claude run exceeded 5 minutes" >&2
exit 2
}
Redirect stderr separately from stdout. The prose answer goes to stdout; diagnostics, warnings, and retry notices go to stderr. Mixing them breaks downstream parsers.
Security considerations
Headless mode plus --dangerously-skip-permissions gives Claude write and shell access without a human in the loop. Treat every invocation as if it were an unreviewed shell script.
Safer defaults for automated runs:
- Run inside a container or a sandboxed worker, not on a developer laptop.
- Restrict the working directory. Claude can only touch files under CWD.
- Pre-filter the prompt. If a user-provided string reaches the prompt verbatim, you are one injection away from trouble.
- Commit the full prompt template to source control and review it like any other code path.
- Capture stdout and stderr to an audit log so unexpected behavior is reviewable after the fact.
- Scope the API key. A dedicated key per automation lets you rotate on incident without breaking the rest of the workflow.
Running headless from cron
A typical crontab entry for a nightly review:
0 2 * * * cd /srv/app && ANTHROPIC_API_KEY=$(cat /etc/secrets/anthropic) timeout 600 claude -p "Audit the last 24 hours of commits for regressions. Write findings to /var/log/ai-review.log" >> /var/log/ai-review.log 2>&1
Points to note: cron has a minimal PATH, so call the binary with a full path if needed. Cron does not source your shell profile, so environment variables must be set inline or loaded from a file. Keep the log file rotated or it will grow without bound.
Combining with other tools
Headless Claude is most useful when chained with other CLI tools. A few patterns that work well:
git log | claude -p "summarize" | mail -s "daily digest" team@example.comjq '.errors' logs.json | claude -p "group by root cause, output a markdown table"curl https://api.example.com/stats | claude -p "flag anomalies, output JSON with a confidence score"
Each pipe step is plain text in, plain text out, which fits the Unix philosophy. That is why headless Claude slots into existing shell pipelines without needing special adapters.
When to graduate to something richer
Once a headless run grows past 50 lines of prompt or needs memory across invocations, move to a proper agent framework. The Anthropic SDK gives you finer control over tool calls, streaming, and caching. Headless Claude Code is the right fit for single-shot or loosely coupled tasks where a shell command is enough.
Frequently asked questions
Does headless mode support OAuth via claude.ai?
No. OAuth expects a browser callback, which cannot happen in a non-interactive shell. Use an `ANTHROPIC_API_KEY` environment variable for all headless and CI runs.
How do I get token usage for a headless run?
Pass `--output-format json` and parse the `.usage` object with jq. It contains `input_tokens`, `output_tokens`, and cache stats. Multiply by per-million rates to get dollar cost.
Is `--dangerously-skip-permissions` safe to use?
Only inside a sandbox you control, such as a CI runner, a Docker container, or a locked-down VM. It disables the approval UI, so any prompt injection becomes shell injection. Never run it on a dev laptop against production paths.
Can headless mode use MCP servers?
Yes. MCP servers configured in `.claude/settings.json` load automatically. Keep their tool scope tight since no human is there to catch a misfire.
What happens when the context window fills up?
The call fails with a context-length error. There is no auto-compaction in headless mode. Keep prompts scoped and pipe only the input you need, rather than dumping the whole repo.
How do I debug a headless run that gave a wrong answer?
Rerun with `--output-format json` and inspect the turns array. You can see every tool call the agent made, which files it read, and which shell commands it ran. Save the JSON log for a postmortem.