← Blog

Claude Code Headless Mode: Run claude -p in Scripts

October 5, 2026

AI Agents & ToolsAI Coding

Claude Code headless mode is one flag: -p. Add it to claude and the agent reads your prompt, does the work, prints the answer to stdout, and exits. No terminal UI, no one at the keyboard. That makes it usable from a shell script, a cron job, a git hook, or a CI step.

Everything below comes from Anthropic's own pages, checked in October 2026. Flags change fast in this tool, so treat the CLI reference as the final word on spelling.

You want to...Use
Run one prompt and get text backclaude -p "your prompt"
Feed it a file or command outputcat file | claude -p "what to do"
Parse the result in a script--output-format json, then jq
Force a specific JSON shape--output-format json --json-schema '...'
Pre-approve tools--allowedTools "Read,Edit"
Cap the run--max-turns 3, --max-budget-usd 5.00
Deny anything that would prompt--permission-mode dontAsk
Keep going in a later call--continue or --resume SESSION_ID
Get the same result on every machine--bare

Is "headless mode" still the name?

Mostly, no. As of October 2026, the page Anthropic maintains for this feature is titled Run Claude Code programmatically. Its URL still ends in /headless, but the word does not appear in the page text. The page calls it non-interactive mode, and the CLI reference describes --print as printing the response "without interactive mode".

The page also frames claude -p as the Agent SDK used through the CLI. If you are choosing between the CLI and the Python or TypeScript packages, our Claude Agent SDK explainer covers the install, the packages, and the migration from the old Claude Code SDK name. This post stays on the command line.

So when you search for headless mode, claude -p, print mode, or non-interactive mode, you are looking at the same thing under four names.

The basics: a prompt, a pipe, an exit code

The smallest version is the one from Anthropic's docs:

claude -p "What does the auth module do?"

-p and --print are the same flag. The prompt is an argument, and the answer lands on stdout, so redirecting it works like any other command.

Non-interactive mode also reads stdin, which is how you hand Claude a log, a diff, or a file without giving it permission to go and fetch it:

cat build-error.txt | claude -p 'concisely explain the root cause of this build error' > output.txt

Two limits are worth knowing. Piped stdin is capped at 10MB, and past that Claude Code exits with an error and a non-zero status. For anything bigger, write the content to a file and name the path in the prompt. The second limit is the pipe itself: piping a diff in means Claude does not need Bash permission to read it, which is why the docs' own typo-linter example does exactly that.

On exit codes, the docs say Claude Code exits 0 on success and non-zero when the run fails, so a script can branch on the status. There is one wrinkle. An invalid flag is reported on stderr before the run starts, but a failure inside the run, such as missing authentication, is printed as the result on stdout. In CI, check the exit code and read stdout when it is not zero.

If you stop a run with SIGTERM, Claude Code exits with code 143 and records no result for the turn in progress. Send SIGINT first if you want the turn to end cleanly.

Output formats and structured output

--output-format takes three values:

FormatWhat you get
text (default)Plain text
jsonOne JSON object with the result, a session ID, and metadata
stream-jsonNewline-delimited JSON events, for real-time streaming

With json, the answer sits in the result field, so jq pulls it out. The docs also say the JSON payload includes total_cost_usd and a per-model cost breakdown, which makes it easy to log spend from a script. They add that these figures are client-side estimates and can differ from your actual bill.

When you need a predictable shape rather than prose, add --json-schema with a JSON Schema definition. The validated output then arrives in the structured_output field:

claude -p "Extract the main function names from auth.py" \
  --output-format json \
  --json-schema '{"type":"object","properties":{"functions":{"type":"array","items":{"type":"string"}}},"required":["functions"]}' \
  | jq '.structured_output'

An invalid schema makes claude exit with an error instead of quietly returning free text. The details live in Anthropic's structured outputs guide.

For streaming, stream-json needs --verbose, and you add --include-partial-messages if you want tokens as they arrive. The last line of the stream is a result message with the final text, cost, and session data.

Tool permissions: where unattended runs go wrong

An interactive session stops and asks before it edits a file or runs a command. A script cannot answer. So every headless run needs an answer to one question: what is Claude allowed to do without asking?

There are three tools for that.

--allowedTools pre-approves specific tools. Listing Read and Edit lets Claude read and edit without a prompt. You can scope Bash with prefix rules, and the docs show a commit example that approves only git diff, git log, git status, and git commit. The trailing space before the * matters: Bash(git diff *) matches commands that start with git diff, while Bash(git diff*) would also match git diff-index.

--permission-mode sets a baseline for the whole run. The modes the docs list for scripts are:

  • acceptEdits: Claude writes files without prompting, plus common filesystem commands such as mkdir, touch, mv, and cp. Other shell commands and network requests still need an allow rule.
  • dontAsk: anything that would prompt is denied. Reads and pre-approved tools still run. The docs call this the one for locked-down CI.
  • auto: a classifier reviews most actions in place of a person.

--permission-prompts none is for runs with nobody to answer, such as a scheduled job. Anything that would prompt is denied, and Claude is told not to retry it. Anthropic notes it needs Claude Code v2.1.259 or later.

The docs' example for a CI run with an exact allowlist combines the first two:

claude -p "run the test suite" --permission-mode dontAsk --allowedTools "Bash(npm test)" "Read"

Do not rely on the default mode. The docs say a -p run's starting mode depends on how Claude Code is set up and which version you run, so pass the one you want. And --dangerously-skip-permissions exists, but the docs list a container, VM, or sandbox runtime as required for it, and a non-root user on Linux and macOS. For the full rule syntax and the modes behind these flags, see our guide to Claude Code permissions.

Limit turns and spend

Two flags put a ceiling on a run, and both are print-mode only:

  • --max-turns limits the number of agentic turns and exits with an error when the limit is reached. There is no limit by default.
  • --max-budget-usd sets a maximum dollar amount to spend on API calls before stopping. Subagent spend counts toward it.

If a script runs unattended, set at least one. A prompt that loops on a flaky test will otherwise run until something else stops it. When --max-turns fires, the run ends with an error, so check the exit status rather than assuming that stdout holds a finished answer.

Resume a session across calls

Each claude -p call is a fresh conversation unless you say otherwise. Two flags continue one:

# First call: capture the session ID
session_id=$(claude -p "Start a review" --output-format json | jq -r '.session_id')

# Later call: pick that conversation back up
claude -p "Continue that review" --resume "$session_id"

# Or just continue the most recent one in this directory
claude -p "Now focus on the database queries" --continue

--continue takes the most recent conversation. --resume takes a specific session ID. Per the docs, you can run the two commands from different directories, since Claude Code finds the session by its ID in any project on the machine. If you do not want anything kept, --no-session-persistence stops the session being saved to disk, which also means it cannot be resumed.

Make CI runs repeatable with --bare

Without --bare, claude -p loads the same context an interactive session would: hooks, skills, plugins, MCP servers, auto memory, and CLAUDE.md from the working directory and your home folder. That is handy on your laptop and a trap on a build server, because a hook in someone else's setup changes the result.

Add --bare and Claude Code skips all of that auto-discovery. Claude keeps its Bash, file read, and file edit tools, and you pass any context you want through flags such as --append-system-prompt, --settings, and --mcp-config. Anthropic recommends --bare for scripted and SDK calls and says it will become the default for -p in a future release.

The catch: bare mode never reads your OAuth login or the keychain. Authentication has to come from ANTHROPIC_API_KEY or an apiKeyHelper. That is also the right shape for CI, where you want a key in a secret store rather than a person's login.

There is a security reason too. The docs say that without --bare, a -p session runs the hooks in a project's .claude/settings.json and connects the servers in its .mcp.json, even in a folder you have never trusted, with no trust dialog. If your script runs Claude over a repository you did not write, that is a reason to use it.

Putting it together, a small review script might look like this:

#!/usr/bin/env bash
set -euo pipefail
# Needs ANTHROPIC_API_KEY in the environment (bare mode ignores subscription login)

git diff main | claude --bare -p \
  "List risky changes in this diff, one per line. Return nothing else." \
  --allowedTools "Read" \
  --max-turns 3 \
  --output-format json | jq -r '.result'

set -o pipefail makes the script fail if claude exits non-zero. This is an illustration built from flags the docs list, not a tested pipeline, so adjust the prompt and limits for your repo.

If you would rather not write the workflow YAML yourself, Anthropic's GitHub Action wraps the same engine. Our Claude Code GitHub Actions walkthrough covers that route, and Anthropic's GitHub Actions page is the source for it.

Where Taku fits

Headless mode answers "how do I run the agent without me in it." It does not answer "which setup is worth running." A prompt, an allowlist, and a schema that work well are usually the product of someone's trial and error.

Taku is an AI-native desktop workspace built around that gap. You mirror an AI setup someone already got working, run it on your machine, remix it, and keep it. It is not a replacement for scripting claude -p in CI, and Claude Code's own flags give you finer control over permissions and budgets. Taku is in Beta, and the Mac app is available now.

FAQ

Is Claude Code headless mode the same as claude -p?

Yes. -p is short for --print, and it runs Claude Code non-interactively: prompt in, answer on stdout, process exits. "Headless mode" is the name most people still search for. Anthropic's current docs page calls it running Claude Code programmatically.

How do I pass a prompt and a file to claude -p?

Put the prompt in the argument and pipe the content in, for example cat build-error.txt | claude -p 'explain this error'. Stdin is capped at 10MB. For larger inputs, reference a file path in the prompt instead.

How do I stop claude -p from asking for permission?

Pre-approve tools with --allowedTools, set a baseline with --permission-mode, or use dontAsk so anything unapproved is denied instead of prompting. In a run with nobody to answer, --permission-prompts none denies requests and tells Claude not to retry them.

How do I get JSON out of claude -p?

Use --output-format json; the answer is in the result field. For a specific shape, add --json-schema and read structured_output. Both are documented in Anthropic's headless page.

Can I limit how long or how much a claude -p run costs?

Yes. --max-turns caps agentic turns and --max-budget-usd caps API spend, both in print mode only. Hitting the turn limit ends the run with an error, so check the exit code.

Does claude -p use my CLAUDE.md and hooks?

By default, yes, plus skills, plugins, and MCP servers. Add --bare to skip all of that auto-discovery so the run behaves the same on every machine.