C.W.K.
Stream
Lesson 02 of 08 · published

query() vs ClaudeSDKClient: Pick by Lifecycle

~16 min · query, ClaudeSDKClient, lifecycle

Level 0Observer
0 XP0/64 lessons0/13 achievements
0/150 XP to next level150 XP to go0% complete

Two APIs for two lifecycles

The SDK exposes two top-level entry points. query(prompt, options) is a one-shot async generator: spin up a subprocess, run a single prompt to completion, tear down. ClaudeSDKClient(options) is a long-lived client: connect once, send many messages, keep the subprocess alive.

When query() fits

Scripts, CI jobs, batch transformations, anything where the agent runs to completion and exits. Each query is independent. cwk-site uses query() for its build-time content generation.

When ClaudeSDKClient fits

Chat applications, long-running operators, anything where multiple messages share state. cwkPippa's Claude brain holds one ClaudeSDKClient per conversation_id — the subprocess persists, the conversation continues, the SDK replays internal history without you resending it.

Principle: query() for one-shot. Client for the long haul. Mixing them is fine; using the wrong one for your lifecycle is wasteful.

Code

One-shot with query()·python
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions

async def explain_file(path: str):
    options = ClaudeAgentOptions(
        cwd="/Users/me/repo",
        allowed_tools=["Read"],  # only read; no edits
    )
    async for event in query(
        prompt=f"Read {path} and write a one-paragraph explanation of what it does.",
        options=options,
    ):
        if hasattr(event, "text"):
            print(event.text, end="")

asyncio.run(explain_file("src/main.py"))
Long-lived ClaudeSDKClient·python
import asyncio
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions

async def chat_session():
    client = ClaudeSDKClient(
        options=ClaudeAgentOptions(
            cwd="/Users/me/repo",
            system_prompt={"type": "preset", "preset": "claude_code"},
        )
    )
    await client.connect()
    try:
        # Multi-turn conversation; the SDK remembers everything internally.
        async for event in client.send_message("What does this repo do?"):
            print_event(event)
        async for event in client.send_message("Add a CLI flag for verbose output."):
            print_event(event)
    finally:
        await client.disconnect()

asyncio.run(chat_session())

External links

Exercise

For one feature you have built or want to build, decide between query() and ClaudeSDKClient and write the one-line justification. If you already coded it, check whether you used the right one — and switch if not.
Hint
The smell test — if your one-shot script is slower than it should be, you probably used a Client; if your chat keeps losing context, you probably used query.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.