Headless mode: Claude Code without the TUI
The fastest way to run Claude Code in the background is also the oldest: the -p flag. No interactive prompt, no TUI, no human watching. Just Claude Code, a task, and an exit code.
The one-liner
claude -p "Summarize what changed in the last 10 commits on this branch"That's the whole API. -p (short for --print) tells Claude Code to skip the interactive session, process the prompt, print the result to stdout, and exit. Everything else is guardrails.
The four flags that matter in production
A bare claude -p call will cheerfully spend your entire monthly budget debugging one stubborn typo. You want these four flags on every unattended invocation:
--allowedTools
By default Claude Code will ask before running any tool. In headless mode, there is no “asking.” Either you pre-authorize the tools you trust, or it freezes. Be specific:
claude -p "Run tests and fix anything that breaks" \
--allowedTools "Bash(npm test),Edit,Read"Note the scoped bash — Bash(npm test) allows only npm test, not arbitrary shell. Scope aggressively.
--max-turns
Caps how many tool calls Claude can make before giving up. 40 is a sane ceiling for a CI job. 5 for a pre-commit hook. 120 for an overnight refactor. Pick a number you'd be okay wasting.
--max-budget-usd
A hard dollar ceiling. The run aborts when it hits the cap. Set it to the cost of the mistake you can tolerate, not the cost of the happy path.
--output-format json
Without this, stdout is human-readable prose and you'll end up parsing it with regex. With it, you get a structured envelope: messages, tool calls, tokens, final answer. Pipe it to jq and your automation stops being hostage to prompt wording.
claude -p "Review this diff" \
--output-format json \
| jq -r '.messages[-1].content'A production-shaped invocation
claude -p "$PROMPT" \
--allowedTools "Bash(git:*),Bash(npm test),Edit,Read,Grep" \
--max-turns 40 \
--max-budget-usd 2 \
--output-format json \
> result.json \
2> claude.errStdout is your structured result. Stderr is Claude's progress log — archive it, don't grep it for control flow. Exit code tells you success vs. budget/turn limit vs. hard failure.
Where headless mode ends
Headless mode is a single process, one prompt in, one result out. That's the right tool for:
- Pre-commit hooks that tidy a single file
- CI jobs that summarize a PR
- One-off batch transforms in a script
- A cron entry that emails you the day's work
It is the wrong tool for:
- Long-running agents that need to survive process restarts
- Work that needs mid-run approval (“about to drop a table, okay?”)
- Parallel issues that shouldn't fight over the same branch
- Anything where “who ran what” is an audit question
For those, you want either scheduled tasks with real guardrails or a full background agent loop.
Further reading
- Claude Code pricing — what headless runs cost, and the flags that cap the bill.
- Turn claude -p into a multi-turn REPL — named pipes + stream-json = interactive headless.
- Claude Code --bare: the flag the docs want to be the default — the reproducibility companion to every guide on this page.
- stream-json: the output format that changes everything — when you want to watch a headless run in real time.
- The 6 Claude Code permission modes —
dontAskis the only CI-safe mode, and this post explains why. - Claude Code in GitHub Actions (v1) — the CI recipe with guardrails.
- The full Claude Code CLI reference — every flag you might want, organized by intent.
Cyrus wraps all of this for you.
Allowed-tool scoping, budgets, isolation, audit logs, approvals, two-way sync with Linear and GitHub — the stuff you'd otherwise script yourself, already shipped. Setup in minutes.
Try Cyrus free →