# claude -p: Claude Code in scripts

Course: Claude Code: Pro Level — https://zero2vibecode.com/learn/claude-code-pro
Canonical URL: https://zero2vibecode.com/learn/claude-code-pro/3-1-claude-p
Verified against: Claude Code v2.1.306 (2026-10-22)
Last updated: 2026-10-22

claude -p runs one request with no interface, prints the answer and exits — this is how Claude Code fits into scripts. With --output-format json the result can be parsed, and the permission mode should always be passed.

So far Claude Code has worked in a dialogue: you ask, it answers, you approve. But sometimes you need Claude **with nobody on the other end** — in a script, on a schedule, in CI. That is what non-interactive mode is for.

## One request, one run

```bash
claude -p "In one sentence: what does this repository do?"
```

Claude Code runs the request, prints the answer and exits: with code `0` if everything went fine, non-zero on failure. It also reads stdin, so a script can feed it, for example, a diff:

```bash
git diff | claude -p "Find obvious mistakes in this diff" --permission-mode dontAsk
```

## An answer you can parse

With the `--output-format json` flag you get a single JSON object instead of text:

```json
{"type":"result","subtype":"success","is_error":false,"num_turns":2,"result":"…","session_id":"…","permission_denials":[]}
```

- `result` — the answer itself;
- `is_error` — whether there was an error;
- `subtype` — `success` or the reason for an early stop, such as `error_max_turns`;
- `num_turns` — how many turns it took;
- `permission_denials` — which actions were denied.

**Important:** if the run stopped at a limit, there is no `result` field at all. So check the exit code and `is_error` first, and only then take the answer.

## Always set the mode explicitly

In a script, nobody can answer a permission prompt. So the mode is passed **every time**: without the flag, the run takes `permissions.defaultMode` from your settings files — which can be auto — and with no mode set anywhere it starts in Manual, where every call that needs approval is denied.

Three flags set the limits:

- `--permission-mode dontAsk` — denies anything that would have needed a question in a session;
- `--allowedTools` — lists what is allowed after all, for example `"Bash(npm test *)"`;
- `--max-turns` — caps the number of turns.

```bash
claude -p "Run the tests and tell me how many passed" \
  --output-format json --permission-mode dontAsk \
  --allowedTools "Bash(npm test *)" --max-turns 3
```

The space before `*` matters: `Bash(npm test *)` allows `npm test` with any arguments, including running it with none.

**Careful.** 
claude -p does not show the trust dialog: it runs the hooks from the project's .claude/settings.json and connects the servers from .mcp.json even in a folder you have never trusted. When you run Claude from a script in someone else's repository, add --setting-sources user — then the project's settings and MCP servers are not loaded. And remember: if the ANTHROPIC_API_KEY variable is set, such a run spends that key, not your subscription.

**Practice in the terminal.** Run claude -p with a request in quotes and the flags --output-format json and --permission-mode dontAsk. Try it without --permission-mode as well — the practice terminal will warn you why that is risky.
