Skip to main content

Overview

The query() function is the simplest way to interact with Claude Code. It’s perfect for one-shot questions, batch processing, and scenarios where you know all your inputs upfront.
For interactive conversations with back-and-forth messaging, use ClaudeSDKClient instead.

When to Use query()

Use query() when you need:
  • Simple one-off questions - “What is 2+2?”
  • Batch processing - Processing multiple independent prompts
  • Code generation - Creating files or analyzing code
  • Automation scripts - CI/CD pipelines, automated tasks
  • Fire-and-forget - When all inputs are known upfront

Basic Example

Here’s the simplest way to use query():
import asyncio
from claude_agent_sdk import query

async def main():
    # Simple question
    async for message in query(prompt="What is the capital of France?"):
        print(message)

asyncio.run(main())

Working with Message Types

The query() function yields different message types. Here’s how to handle them:
from claude_agent_sdk import (
    query,
    AssistantMessage,
    ResultMessage,
    TextBlock
)

async def main():
    async for message in query(prompt="Explain Python async/await in simple terms"):
        if isinstance(message, AssistantMessage):
            # Extract text from Claude's response
            for block in message.content:
                if isinstance(block, TextBlock):
                    print(f"Claude: {block.text}")
        
        elif isinstance(message, ResultMessage):
            # Final message with metadata
            print(f"\nCost: ${message.total_cost_usd:.4f}")
            print(f"Status: {message.stop_reason}")

Using Options

Customize Claude’s behavior with ClaudeAgentOptions:
from claude_agent_sdk import query, ClaudeAgentOptions

options = ClaudeAgentOptions(
    system_prompt="You are an expert Python developer.",
    model="claude-sonnet-4-5"
)

async for message in query(
    prompt="Review this code for best practices",
    options=options
):
    print(message)

Permission Modes

Control how Claude handles potentially dangerous operations:
1

Default Mode

Claude prompts before executing dangerous operations:
options = ClaudeAgentOptions(
    permission_mode="default"
)
2

Accept Edits

Automatically accept file edits (but still prompt for other operations):
options = ClaudeAgentOptions(
    permission_mode="acceptEdits"
)
3

Bypass Permissions

Allow all operations without prompts (use with caution):
options = ClaudeAgentOptions(
    permission_mode="bypassPermissions"
)

Complete Example

Here’s a practical example that creates a file and handles all message types:
import asyncio
from claude_agent_sdk import (
    query,
    ClaudeAgentOptions,
    AssistantMessage,
    UserMessage,
    SystemMessage,
    ResultMessage,
    TextBlock,
    ToolUseBlock,
    ToolResultBlock
)

async def create_hello_file():
    """Create a hello.txt file using Claude."""
    
    options = ClaudeAgentOptions(
        allowed_tools=["Write"],  # Allow file operations
        permission_mode="acceptEdits",  # Auto-accept edits
        system_prompt="You are a helpful assistant."
    )
    
    print("Creating hello.txt file...\n")
    
    async for message in query(
        prompt="Create a simple hello.txt file with a greeting message",
        options=options
    ):
        if isinstance(message, AssistantMessage):
            for block in message.content:
                if isinstance(block, TextBlock):
                    print(f"Claude: {block.text}")
                elif isinstance(block, ToolUseBlock):
                    print(f"Using tool: {block.name}")
        
        elif isinstance(message, UserMessage):
            # Tool results come back as UserMessage
            for block in message.content:
                if isinstance(block, ToolResultBlock):
                    print(f"Tool result: Success")
        
        elif isinstance(message, ResultMessage):
            print(f"\n✓ Task completed")
            if message.total_cost_usd:
                print(f"Cost: ${message.total_cost_usd:.6f}")

if __name__ == "__main__":
    asyncio.run(create_hello_file())

Streaming Mode

For advanced use cases, you can stream multiple prompts:
import asyncio
from claude_agent_sdk import query

async def stream_prompts():
    """Stream multiple prompts to Claude."""
    yield {
        "type": "user",
        "message": {"role": "user", "content": "What's the capital of Japan?"},
        "parent_tool_use_id": None,
        "session_id": "session-1"
    }
    
    yield {
        "type": "user",
        "message": {"role": "user", "content": "What about France?"},
        "parent_tool_use_id": None,
        "session_id": "session-1"
    }

async def main():
    async for message in query(prompt=stream_prompts()):
        print(message)

asyncio.run(main())
Streaming mode is still unidirectional - all prompts are sent first, then all responses are received. For true bidirectional communication, use ClaudeSDKClient.

Best Practices

Always handle AssistantMessage, UserMessage, SystemMessage, and ResultMessage types to capture the full conversation flow.
Use the most restrictive permission mode that works for your use case. Start with default and only escalate if needed.
When working with files, always set cwd in options to ensure Claude operates in the correct directory.
Use allowed_tools to restrict which tools Claude can use, improving security and reducing unexpected behavior.
Provide clear context through system_prompt to guide Claude’s responses and improve result quality.

Next Steps

Interactive Conversations

Learn how to use ClaudeSDKClient for bidirectional conversations

Custom Tools

Create custom tools to extend Claude’s capabilities

Configuration Options

Explore all available ClaudeAgentOptions

Message Types

Deep dive into message types and content blocks

Build docs developers (and LLMs) love