# Claude Agent SDK for Python: Your First Agent

> Install claude-agent-sdk, run an agent that reads and edits real files, control which tools it may use, and read the final result.

- Canonical: https://guides-ai.pages.dev/guides/claude-agent-sdk-python-quickstart/
- Plate 15.09 · Topic: Building with the API (https://guides-ai.pages.dev/topics/api/)
- Published: 06 Sept 2026 · 4 min read
- Source site: guides-ai — https://guides-ai.pages.dev/

The Claude Agent SDK is Claude Code packaged as a library: the agent loop, the built-in
Read/Edit/Bash/Glob/Grep tools, context management and permissions, driven from your own
code. It is **not** the Anthropic API SDK — different package, different job.

## 1. Install

Python 3.10 or later.

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install claude-agent-sdk
```

```powershell
py -m venv .venv
.venv\Scripts\Activate.ps1
pip install claude-agent-sdk
```

What it does: installs the SDK plus a bundled Claude Code binary, so there's usually no
separate install. If pip falls back to the source distribution (ARM64 Windows, for example),
install Claude Code natively and the SDK will find it on your `PATH`. If PowerShell blocks
`Activate.ps1`, run `Set-ExecutionPolicy -Scope Process RemoteSigned` first.

## 2. Set the key

```bash
export ANTHROPIC_API_KEY=your-api-key
```

What it does: authenticates the agent. The SDK reads it from the process environment and
does **not** load `.env` files for you — load them yourself with `dotenv` if that's where
the key lives.

## 3. Write the agent

`agent.py`:

```python
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ResultMessage


async def main():
    async for message in query(
        prompt="Review utils.py for bugs that would cause crashes. Fix any issues you find.",
        options=ClaudeAgentOptions(
            allowed_tools=["Read", "Edit", "Glob"],
            permission_mode="acceptEdits",
        ),
    ):
        if isinstance(message, AssistantMessage):
            for block in message.content:
                if hasattr(block, "text"):
                    print(block.text)
                elif hasattr(block, "name"):
                    print(f"Tool: {block.name}")
        elif isinstance(message, ResultMessage):
            print(f"Done: {message.subtype}")


asyncio.run(main())
```

What it does: `query()` returns an async iterator, so the `async for` streams messages as
Claude thinks, calls tools and observes results. `AssistantMessage.content` holds the
blocks — text and tool calls; `ResultMessage` arrives last and carries the outcome
(`subtype`, plus `terminal_reason` for *why* the loop stopped).

## 4. Run it

```bash
python agent.py
```

What it does: runs the loop against files in the current directory and its subdirectories,
which is the agent's default working scope. Watch the file actually change on disk — that's
the proof it worked.

## 5. Tighten it

`allowed_tools` is an allowlist: listed tools are auto-approved, anything else falls through
to `permission_mode`. Drop `Edit` for read-only analysis; add `Bash` only when the agent
genuinely needs to run tests. Other useful options: `system_prompt`, `max_turns`, `cwd`,
`mcp_servers`. For a multi-turn conversation that keeps context between prompts, use
`ClaudeSDKClient` instead of `query()`.

---

Next: [the same agent in TypeScript](/guides/claude-agent-sdk-typescript-quickstart/) or
[tool use with the Claude API](/guides/claude-api-tool-use/).
