F5.4 F5

When Statefulness Actually Matters

Choosing between query() and ClaudeSDKClient is not about features, performance, or language. Both support tools. Both accept the same options. Both use the same model. The only question is: does the next call need context from the previous one?

The decision rule

Does the next interaction reference the previous one? If yes → ClaudeSDKClient. If no → query().

A debugging session: “read auth.ts” → “now check if that function is called anywhere.” The second request only makes sense with context from the first. That’s ClaudeSDKClient.

Processing 100 invoices: each extraction is independent. Invoice #47 has nothing to do with invoice #12. That’s 100 query() calls.

Common misconceptions

“query() can’t use tools” — Wrong. query() runs the full agentic loop. If a task requires calling Read, then Grep, then Write, all of that happens within one query() invocation. Tool chains within a single execution work fine. The limitation is only about cross-call context — the next query() call doesn’t know what the previous one did.

“ClaudeSDKClient is faster/better” — The choice is about state management, not performance. There’s no 30-second timeout on query(). There’s no enhanced error handling in ClaudeSDKClient. Both run the same agentic loop with the same model.

“Use ClaudeSDKClient everywhere for consistency” — This adds session lifecycle overhead (async context manager setup/teardown) for tasks that don’t benefit from it. One-shot tasks are simpler with query().

The right match

TaskInterfaceWhy
Classify one ticketquery()Self-contained, no follow-up
Process 50 independent invoices50× query()Each is independent
Multi-turn customer supportClaudeSDKClientFollow-ups reference prior context
Debugging session with back-and-forthClaudeSDKClientEach step builds on previous
One-shot document analysisquery()Complete in one interaction

Both accept ClaudeAgentOptions

Both interfaces take the same configuration object. System prompt, allowed tools, permission mode — all configurable for both. The interface choice is orthogonal to the configuration.


One-liner: Choose query() when each call is independent and ClaudeSDKClient when follow-ups need context from prior exchanges — both support tools, same model, same configuration.