Getting Started with the Claude Code API: A Complete Tutorial

Claude Code is no longer just a terminal tool — it's a full agentic API. This tutorial shows you how to go from your first API call to building autonomous coding agents in Python or TypeScript.

M

Muunsparks

2026-03-17

7 min read

howtouseclaudeapi

Anthropic's Claude Code has evolved far beyond a terminal coding assistant. With the Claude Agent SDK, you can now embed Claude Code's full agentic capabilities — file reading, shell execution, web search, and more — directly into your own applications. This post walks you through everything you need to get up and running.


What Is the Claude Code API?

There are two ways developers commonly interact with Claude programmatically:

  • The Messages API — a RESTful API at https://api.anthropic.com/v1/messages for sending prompts and getting responses. Great for chatbots, text generation, and tool-use pipelines you control manually.
  • The Claude Agent SDK — a higher-level SDK that wraps the full Claude Code agentic loop. You hand it a task; it autonomously runs tools (Read, Write, Edit, Bash, and more) until the job is done.

This tutorial covers both, starting with the simpler Messages API before moving on to the Agent SDK.


Prerequisites

  • An Anthropic Console account at console.anthropic.com
  • An API key (generate one in Account Settings)
  • Python 3.9+ or Node.js 18+ depending on your language of choice

Set your API key as an environment variable so you never hard-code it:

export ANTHROPIC_API_KEY="sk-ant-..."

Part 1: The Messages API

The Messages API is your starting point for any Claude integration. It's a simple POST request.

Installation

Python:

pip install anthropic

TypeScript / Node.js:

npm install @anthropic-ai/sdk

Your First API Call

Python:

import anthropic

client = anthropic.Anthropic()  # Reads ANTHROPIC_API_KEY from environment

message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[
        {"role": "user", "content": "Explain async/await in Python in two sentences."}
    ],
)

print(message.content[0].text)

TypeScript:

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic(); // Reads ANTHROPIC_API_KEY from environment

const message = await client.messages.create({
  model: "claude-sonnet-4-6",
  max_tokens: 1024,
  messages: [
    {
      role: "user",
      content: "Explain async/await in Python in two sentences.",
    },
  ],
});

console.log(message.content[0].text);

Or via raw cURL:

curl https://api.anthropic.com/v1/messages \
  --header "x-api-key: $ANTHROPIC_API_KEY" \
  --header "anthropic-version: 2023-06-01" \
  --header "content-type: application/json" \
  --data '{
    "model": "claude-sonnet-4-6",
    "max_tokens": 1024,
    "messages": [{"role": "user", "content": "Hello, Claude"}]
  }'

Choosing a Model

| Model | Best For | |---|---| | claude-opus-4-6 | Complex reasoning, deep analysis, difficult coding | | claude-sonnet-4-6 | Balanced intelligence and speed — ideal for most production workloads | | claude-haiku-4-5 | Fast, lightweight tasks at lower cost |

Streaming Responses

For a better user experience (especially with longer outputs), use streaming:

with client.messages.stream(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Write me a sorting algorithm in Python"}],
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)

Tool Use (Function Calling)

Claude can call tools you define. Here's a minimal example:

tools = [
    {
        "name": "get_file_contents",
        "description": "Read the contents of a file by path",
        "input_schema": {
            "type": "object",
            "properties": {
                "path": {"type": "string", "description": "The file path to read"}
            },
            "required": ["path"],
        },
    }
]

response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=tools,
    messages=[{"role": "user", "content": "What's in my README.md?"}],
)

# Check if Claude wants to call a tool
if response.stop_reason == "tool_use":
    for block in response.content:
        if block.type == "tool_use":
            print(f"Tool called: {block.name}")
            print(f"With input: {block.input}")

Part 2: The Claude Agent SDK

The Agent SDK is where things get really powerful. Instead of managing the tool loop yourself, you delegate an entire task to Claude and let the SDK handle the back-and-forth autonomously.

Installation

Python:

pip install claude-agent-sdk

TypeScript / Node.js:

npm install @anthropic-ai/claude-code

Note: The Claude Code CLI is automatically bundled with the Python package — no separate installation required.

Simple Query

The query() function is an async generator that yields messages as Claude works through your task:

Python:

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, TextBlock

async def main():
    async for message in query(prompt="List all Python files in this directory"):
        if isinstance(message, AssistantMessage):
            for block in message.content:
                if isinstance(block, TextBlock):
                    print(block.text)

asyncio.run(main())

TypeScript:

import { query, ClaudeAgentOptions } from "@anthropic-ai/claude-code";

for await (const message of query({
  prompt: "List all Python files in this directory",
})) {
  if (message.type === "assistant") {
    for (const block of message.content) {
      if (block.type === "text") {
        console.log(block.text);
      }
    }
  }
}

Configuring the Agent with Options

Use ClaudeAgentOptions to control Claude's behavior:

options = ClaudeAgentOptions(
    system_prompt="You are a senior Python engineer. Be concise and precise.",
    max_turns=10,
    cwd="/path/to/my/project",  # Set the working directory
)

async for message in query(
    prompt="Refactor the utils.py file to use dataclasses",
    options=options
):
    print(message)

Managing Tool Permissions

By default, Claude has access to its full toolset: Read, Write, Edit, Bash, and more. You can restrict or auto-approve tools:

options = ClaudeAgentOptions(
    allowed_tools=["Read", "Glob"],      # Auto-approve these — Claude won't ask for confirmation
    disallowed_tools=["Bash"],           # Block these entirely
    permission_mode="acceptEdits",       # Auto-accept file edits
)

async for message in query(
    prompt="Find all TODO comments in the codebase",
    options=options
):
    pass

Permission modes:

| Mode | Behavior | |---|---| | default | Claude asks before each sensitive action | | acceptEdits | Auto-approves file edits, still asks for shell commands | | bypassPermissions | Full auto-approval (use only in trusted environments) |

Custom Tools

One of the Agent SDK's most powerful features is the ability to define your own Python functions as tools that Claude can invoke:

from claude_agent_sdk import ClaudeSDKClient

async def send_slack_notification(channel: str, message: str) -> str:
    """Send a Slack notification to a channel."""
    # Your Slack integration here
    return f"Sent to #{channel}: {message}"

async def main():
    client = ClaudeSDKClient(
        custom_tools=[send_slack_notification]
    )

    async with client:
        response = await client.query(
            "Analyze the test results in test_output.log and notify #engineering on Slack with a summary"
        )
        print(response)

asyncio.run(main())

Custom tools run as in-process MCP servers — no separate process or network setup required.


Part 3: Batch Processing

For high-volume, non-time-sensitive workloads, the Message Batches API processes requests asynchronously at a 50% cost reduction.

batch = client.messages.batches.create(
    requests=[
        {
            "custom_id": "review-pr-1",
            "params": {
                "model": "claude-sonnet-4-6",
                "max_tokens": 1024,
                "messages": [{"role": "user", "content": "Review this diff: ..."}],
            },
        },
        {
            "custom_id": "review-pr-2",
            "params": {
                "model": "claude-sonnet-4-6",
                "max_tokens": 1024,
                "messages": [{"role": "user", "content": "Review this diff: ..."}],
            },
        },
    ]
)

print(f"Batch ID: {batch.id}")
# Poll batch.processing_status until "ended"

Part 4: Real-World Example — Automated Code Review Agent

Putting it all together: an agent that reviews a pull request, writes a summary, and saves it to a file.

import asyncio
from pathlib import Path
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, TextBlock

async def review_pr(diff_path: str, output_path: str):
    options = ClaudeAgentOptions(
        system_prompt="""You are an expert code reviewer. 
        Analyze the provided diff for: correctness, security issues, 
        performance concerns, and code style. Be specific and actionable.""",
        cwd=str(Path(diff_path).parent),
        allowed_tools=["Read", "Write"],
        permission_mode="acceptEdits",
        max_turns=5,
    )

    prompt = f"""
    Please review the git diff at {diff_path}.
    Write a structured code review to {output_path} with sections for:
    - Summary of changes
    - Issues found (critical / minor)
    - Suggestions for improvement
    - Overall assessment
    """

    print("🔍 Starting code review...")
    async for message in query(prompt=prompt, options=options):
        if isinstance(message, AssistantMessage):
            for block in message.content:
                if isinstance(block, TextBlock) and block.text.strip():
                    print(f"  → {block.text[:80]}...")

    print(f"✅ Review saved to {output_path}")

asyncio.run(review_pr("changes.diff", "review.md"))

Key Concepts Recap

Messages API — direct request/response, you manage state and tool loops manually. Best for: chatbots, simple Q&A, tool-use pipelines.

Agent SDK — autonomous task execution with Claude managing the full loop. Best for: complex multi-step coding tasks, file manipulation, automated workflows.

Batch API — async bulk processing at reduced cost. Best for: large-scale analysis, nightly jobs, non-urgent workloads.


Further Reading


Happy building! If you run into issues, the Anthropic Discord and support center are great places to get help.