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
| Task | Interface | Why |
|---|---|---|
| Classify one ticket | query() | Self-contained, no follow-up |
| Process 50 independent invoices | 50× query() | Each is independent |
| Multi-turn customer support | ClaudeSDKClient | Follow-ups reference prior context |
| Debugging session with back-and-forth | ClaudeSDKClient | Each step builds on previous |
| One-shot document analysis | query() | 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.