F5.2 F5

query() vs ClaudeSDKClient: Stateless vs Stateful

The Agent SDK gives you two interfaces. The choice between them comes down to one question: does the next interaction need to know what happened in the previous one?

query(): fire and forget

query() processes one prompt as an independent operation. No session setup, no teardown, no lingering state. You call it, it runs the full agentic loop (including tool calls), and it returns the result. The next query() call has zero knowledge of what the previous one did.

This is the right choice for self-contained tasks: classify a document, extract data from an invoice, answer a standalone question. Each operation is complete in itself.

ClaudeSDKClient: context across turns

ClaudeSDKClient maintains conversation state across multiple query() calls within a session. When you say “look at auth.ts” and then follow up with “now check if that function is called anywhere,” the client knows which function you mean — it kept the context from the first exchange.

It’s used as an async context manager (async with ClaudeSDKClient(...) as client), which handles session lifecycle automatically. State lives in memory during the session. If the process dies, the state is gone — there’s no automatic disk persistence. Surviving restarts requires explicit developer-implemented persistence.

Both support tools

A critical misunderstanding: “query() can’t use tools.” Wrong. query() runs the full agentic loop within a single invocation. If the agent needs to call Read, then Grep, then Edit to complete the task, it does all of that within one query() call. Tool use is not limited to ClaudeSDKClient.

The distinction is about cross-call state, not within-call capabilities. Both interfaces support tools. Both accept ClaudeAgentOptions for configuration. Both use the same model.

Choosing the right interface

  • 100 independent documents to process → 100 query() calls. Each is self-contained. Using ClaudeSDKClient would accumulate irrelevant cross-document context.
  • Customer support conversation with follow-upsClaudeSDKClient. Follow-up questions reference prior context.
  • One-shot ticket classificationquery(). Classify and move on.
  • Debugging session (“read this file, now check that function”) → ClaudeSDKClient. Each step builds on the previous.

Don’t use ClaudeSDKClient for simple tasks just for “consistency” — it adds unnecessary session lifecycle complexity. Don’t use query() with manual history appending for multi-turn — that’s what ClaudeSDKClient automates.


One-liner: Use query() for independent one-shot tasks and ClaudeSDKClient for multi-turn interactions requiring cross-call context — both support tools and the full agentic loop.