Run Claude Code programmatically
Use the Agent SDK to run Claude Code programmatically from the CLI, Python, or TypeScript.
The Agent SDK gives you the same tools, agent loop, and context management that power Claude Code. It's available as a CLI for scripts and CI/CD, or as Python and TypeScript packages for full programmatic control.
INFO
The CLI was previously called "headless mode." The -p flag and all CLI options work the same way.
To run Claude Code programmatically from the CLI, pass -p with your prompt and any CLI options:
claude -p "Find and fix the bug in auth.py" --allowedTools "Read,Edit,Bash"This page covers using the Agent SDK via the CLI (claude -p). For the Python and TypeScript SDK packages with structured outputs, tool approval callbacks, and native message objects, see the full Agent SDK documentation.
Basic usage
Add the -p (or --print) flag to any claude command to run it non-interactively. All CLI options work with -p, including:
--continuefor continuing conversations--allowedToolsfor auto-approving tools--output-formatfor structured output
This example asks Claude a question about your codebase and prints the response:
claude -p "What does the auth module do?"Start faster with bare mode
Add --bare to reduce startup time by skipping auto-discovery of hooks, skills, plugins, MCP servers, auto memory, and CLAUDE.md. Without it, claude -p loads the same context an interactive session would, including anything configured in the working directory or ~/.claude.
Bare mode is useful for CI and scripts where you need the same result on every machine. A hook in a teammate's ~/.claude or an MCP server in the project's .mcp.json won't run, because bare mode never reads them. Only flags you pass explicitly take effect.
This example runs a one-off summarize task in bare mode and pre-approves the Read tool so the call completes without a permission prompt:
claude --bare -p "Summarize this file" --allowedTools "Read"In bare mode Claude has access to the Bash, file read, and file edit tools. Pass any context you need with a flag:
| To load | Use |
|---|---|
| System prompt additions | --append-system-prompt, --append-system-prompt-file |
| Settings | --settings <file-or-json> |
| MCP servers | --mcp-config <file-or-json> |
| Custom agents | --agents <json> |
| A plugin directory | --plugin-dir <path> |
Bare mode skips OAuth and keychain reads. Anthropic authentication must come from ANTHROPIC_API_KEY or an apiKeyHelper in the JSON passed to --settings. Bedrock, Vertex, and Foundry use their usual provider credentials.
INFO
--bare is the recommended mode for scripted and SDK calls, and will become the default for -p in a future release.
Examples
These examples highlight common CLI patterns. For CI and other scripted calls, add --bare so they don't pick up whatever happens to be configured locally.
Get structured output
Use --output-format to control how responses are returned:
text(default): plain text outputjson: structured JSON with result, session ID, and metadatastream-json: newline-delimited JSON for real-time streaming
This example returns a project summary as JSON with session metadata, with the text result in the result field:
claude -p "Summarize this project" --output-format jsonTo get output conforming to a specific schema, use --output-format json with --json-schema and a JSON Schema definition. The response includes metadata about the request (session ID, usage, etc.) with the structured output in the structured_output field.
This example extracts function names and returns them as an array of strings:
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"]}'TIP
Use a tool like jq to parse the response and extract specific fields:
# Extract the text result
claude -p "Summarize this project" --output-format json | jq -r '.result'
# Extract structured output
claude -p "Extract function names from auth.py" \
--output-format json \
--json-schema '{"type":"object","properties":{"functions":{"type":"array","items":{"type":"string"}}},"required":["functions"]}' \
| jq '.structured_output'Stream responses
Use --output-format stream-json with --verbose and --include-partial-messages to receive tokens as they're generated. Each line is a JSON object representing an event:
claude -p "Explain recursion" --output-format stream-json --verbose --include-partial-messagesThe following example uses jq to filter for text deltas and display just the streaming text. The -r flag outputs raw strings (no quotes) and -j joins without newlines so tokens stream continuously:
claude -p "Write a poem" --output-format stream-json --verbose --include-partial-messages | \
jq -rj 'select(.type == "stream_event" and .event.delta.type? == "text_delta") | .event.delta.text'When an API request fails with a retryable error, Claude Code emits a system/api_retry event before retrying. You can use this to surface retry progress or implement custom backoff logic.
| Field | Type | Description |
|---|---|---|
type | "system" | message type |
subtype | "api_retry" | identifies this as a retry event |
attempt | integer | current attempt number, starting at 1 |
max_retries | integer | total retries permitted |
retry_delay_ms | integer | milliseconds until the next attempt |
error_status | integer or null | HTTP status code, or null for connection errors with no HTTP response |
error | string | error category: authentication_failed, billing_error, rate_limit, invalid_request, server_error, max_output_tokens, or unknown |
uuid | string | unique event identifier |
session_id | string | session the event belongs to |
For programmatic streaming with callbacks and message objects, see Stream responses in real-time in the Agent SDK documentation.
Auto-approve tools
Use --allowedTools to let Claude use certain tools without prompting. This example runs a test suite and fixes failures, allowing Claude to execute Bash commands and read/edit files without asking for permission:
claude -p "Run the test suite and fix any failures" \
--allowedTools "Bash,Read,Edit"Create a commit
This example reviews staged changes and creates a commit with an appropriate message:
claude -p "Look at my staged changes and create an appropriate commit" \
--allowedTools "Bash(git diff *),Bash(git log *),Bash(git status *),Bash(git commit *)"The --allowedTools flag uses permission rule syntax. The trailing * enables prefix matching, so Bash(git diff *) allows any command starting with git diff. The space before * is important: without it, Bash(git diff*) would also match git diff-index.
INFO
User-invoked skills like /commit and built-in commands are only available in interactive mode. In -p mode, describe the task you want to accomplish instead.
Customize the system prompt
Use --append-system-prompt to add instructions while keeping Claude Code's default behavior. This example pipes a PR diff to Claude and instructs it to review for security vulnerabilities:
gh pr diff "$1" | claude -p \
--append-system-prompt "You are a security engineer. Review for vulnerabilities." \
--output-format jsonSee system prompt flags for more options including --system-prompt to fully replace the default prompt.
Continue conversations
Use --continue to continue the most recent conversation, or --resume with a session ID to continue a specific conversation. This example runs a review, then sends follow-up prompts:
# First request
claude -p "Review this codebase for performance issues"
# Continue the most recent conversation
claude -p "Now focus on the database queries" --continue
claude -p "Generate a summary of all issues found" --continueIf you're running multiple conversations, capture the session ID to resume a specific one:
session_id=$(claude -p "Start a review" --output-format json | jq -r '.session_id')
claude -p "Continue that review" --resume "$session_id"Next steps
- Agent SDK quickstart: build your first agent with Python or TypeScript
- CLI reference: all CLI flags and options
- GitHub Actions: use the Agent SDK in GitHub workflows
- GitLab CI/CD: use the Agent SDK in GitLab pipelines