·SDKs & Tooling
A beginner-friendly walkthrough of the Claude API — getting an API key, your first messages.create call in Python and TypeScript, streaming responses, and wiring up tool use so Claude can call functions you define.
Getting Started With the Claude API: Your First Call, Streaming, and Tool Use
The Claude API is how you build with Claude inside your own product — a support bot, a code generator, a document summarizer, anything where you control the loop. The good news is that the surface is small: one endpoint, one SDK per language, and a handful of concepts that compose. This guide walks through getting an API key, making your first messages.create call in Python and TypeScript, streaming responses, and wiring up tool use so Claude can call functions you define.
Get an API key
Everything starts at the Anthropic Console. Create an account, then create an API key from the API Keys section. The key starts with sk-ant- and you will only see it once — copy it somewhere safe immediately. Treat it like a password: anyone with the key can spend your credits.
Set the key as an environment variable so the SDK can find it without you hardcoding it into source:
export ANTHROPIC_API_KEY="sk-ant-..."
Add that line to your shell profile so it persists across terminals, or use a .env file and a loader in production. The SDK reads ANTHROPIC_API_KEY automatically when you do not pass a key explicitly, which keeps secrets out of your code.
While you are in the console, set a spending limit. The API bills per token, and it is easy to burn through credit with a runaway loop. A budget cap turns a bug into an error instead of a surprise bill.
Install the SDK
Anthropic maintains official SDKs for Python and TypeScript (plus community SDKs for other languages). Pick the one that matches your stack.
Python:
pip install anthropic
TypeScript / Node.js:
npm install @anthropic-ai/sdk
Both SDKs thin-wrap the same HTTP API, so the concepts transfer exactly between them. The examples below use Python, with the TypeScript equivalent noted where it differs.
Your first message
The API is organized around a single endpoint: create a message. You send a model, a list of messages (each with a role and content), and a max output length, and you get back Claude's response.
from anthropic import Anthropic
client = Anthropic() # picks up ANTHROPIC_API_KEY from the environment
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[
{"role": "user", "content": "Explain what an MCP server is in two sentences."}
],
)
print(response.content[0].text)
A few things worth understanding about this call:
model— the model ID. The current generation includes families like Claude Opus, Sonnet, and Haiku, each in numbered versions. The model IDs follow a pattern likeclaude-sonnet-4-5; always check the official models documentation for the exact, current ID strings, because they evolve over time.max_tokens— the maximum number of tokens the response may contain. This is a hard cap and a required field. Set it generously for open-ended generation and tightly for structured extraction.messages— the conversation as a list of{role, content}turns. Roles areuserandassistant. You do not pass a separate system message here; that goes in an optional top-levelsystemparameter.response.content— the response is a list of content blocks, not a plain string. The most common block type istext, but as you will see with tool use, it can also betool_use. Indexingresponse.content[0].textgets the text of a typical response.
The TypeScript call is the same shape: client.messages.create({ model, max_tokens, messages }), returning an object with a .content array.
Adding a system prompt
A system prompt sets Claude's role, tone, and ground rules for the whole conversation. Pass it as a top-level system argument:
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
system="You are a meticulous copy editor. Correct grammar and clarity, but never change the author's meaning or add information.",
messages=[
{"role": "user", "content": "Review this sentence for clarity: ..."}
],
)
The system prompt is where stable instructions live — the identity, the policies, the output format. The user messages are where the varying input lives. Keeping that separation makes prompts easier to maintain.
Streaming responses
For interactive apps, waiting for a full response feels slow. Streaming lets you emit tokens as they arrive. The Python SDK provides a streaming helper:
with client.messages.stream(
model="claude-sonnet-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Write a short poem about TCP."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
stream.text_stream yields text chunks as they are generated, so you can print them, send them over a websocket, or append them to a UI in real time. Under the hood this uses server-sent events; the SDK handles the parsing for you.
You can also call messages.create(..., stream=True) and iterate over the raw event stream yourself, which gives you finer control over the individual events (message start, content block deltas, message stop). The stream() helper is the easier path for most cases; the raw stream is for when you need to handle every event type explicitly.
Tool use: let Claude call your functions
Tool use (often called "function calling") is what turns Claude from a text generator into an agent that can take actions. You define tools with a name, a description, and a JSON Schema for the inputs. When Claude decides a tool would help answer the user, it emits a tool_use block instead of (or alongside) text. You execute the tool, send back a tool_result, and Claude continues.
Here is a complete, minimal example. We define a get_weather tool and let Claude call it:
import json
from anthropic import Anthropic
client = Anthropic()
tools = [
{
"name": "get_weather",
"description": "Get the current weather for a given city.",
"input_schema": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "The city name, e.g. 'San Francisco'."}
},
"required": ["city"],
},
}
]
# In a real app this would call an actual weather API.
def get_weather(city: str) -> str:
return json.dumps({"city": city, "condition": "foggy", "temperature_c": 14})
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
tools=tools,
messages=[{"role": "user", "content": "What's the weather in San Francisco?"}],
)
# When Claude decides to use a tool, the stop_reason is "tool_use" and a
# tool_use block appears in the response content.
if response.stop_reason == "tool_use":
# Find the tool_use block (there can be more than one in a single response).
for block in response.content:
if block.type == "tool_use":
result = get_weather(**block.input)
# Send the assistant's tool_use turn back, followed by a user turn
# containing the tool_result. Then call create again so Claude can
# use the result to answer.
follow_up = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
tools=tools,
messages=[
{"role": "user", "content": "What's the weather in San Francisco?"},
{"role": "assistant", "content": response.content},
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": block.id,
"content": result,
}
],
},
],
)
print(follow_up.content[0].text)
else:
print(response.content[0].text)
The mechanics that matter:
input_schemais a JSON Schema describing the tool's arguments. Claude fills ininputfollowing that schema, so a precise schema produces precise calls.tool_use_idties the result to the call. If Claude makes two tool calls in one response, each result needs the matching id.- You return the assistant turn intact. Notice the follow-up messages include
{"role": "assistant", "content": response.content}— the full original content blocks, not just extracted text. This preserves the tool-use context so Claude knows what it asked for. stop_reason == "tool_use"is how you detect that Claude wants to call a tool rather than answer directly.
In a production agent loop you keep calling create until stop_reason is no longer tool_use, executing tools and feeding back results each turn. That loop is the heart of any agent — and it is also what the Claude Agent SDK implements for you if you prefer not to write it yourself.
Practical tips
A few habits that pay off fast:
- Check the model ID before shipping. IDs evolve; a hardcoded ID from a tutorial may be stale. The official models documentation is the source of truth.
- Set
max_tokensdeliberately. Too low and Claude's answer gets cut off mid-sentence; too high and you give up cost control. For structured extraction, a tight cap also nudges the model toward concise output. - Log the full request and response during development. When something goes wrong, the request body and the raw response are usually enough to diagnose it. Strip the logging before production to avoid logging user data.
- Handle errors and rate limits. The SDK raises typed exceptions for errors, rate limits, and overloaded servers. Retry with backoff on the retryable ones.
- Start without tools, add them when needed. A surprising amount can be done with a good system prompt and well-structured input. Add tool use when the model genuinely needs to take an action or look something up it could not know.
Frequently asked questions
How do I get an API key for the Claude API?
Create an account at the Anthropic Console and generate a key from the API Keys section. The key starts with sk-ant- and is shown only once, so copy it immediately. Set it as the ANTHROPIC_API_KEY environment variable so the SDK reads it automatically, and set a spending limit in the console to cap your exposure.
What model name should I pass to messages.create?
The model IDs follow a family-and-version pattern, such as claude-sonnet-4-5 for the Sonnet family. The exact, current ID strings evolve over time as new versions ship, so always check the official models documentation for the latest IDs rather than relying on a hardcoded value from a tutorial.
How does tool use work with the Claude API?
You pass a tools array to messages.create, where each tool has a name, a description, and a JSON Schema for its inputs (input_schema). When Claude decides a tool would help, it returns a tool_use content block and sets stop_reason to 'tool_use'. You execute the tool, then send a follow-up message containing a tool_result block (with the matching tool_use_id), and call create again so Claude can use the result to answer.
What is the difference between messages.create with stream=True and messages.stream?
Both stream the response. messages.stream is a higher-level helper that yields parsed text chunks through stream.text_stream and handles the event parsing for you — easiest for most cases. messages.create(..., stream=True) returns the raw event stream, giving you finer control over individual events like content block deltas; use it when you need to handle every event type explicitly.
Official references
- Claude API documentation overview
- Anthropic Python SDK (anthropics/anthropic-sdk-python)
- Anthropic TypeScript SDK (anthropics/anthropic-sdk-typescript)
- Claude tool use overview
- Claude models overview
This article reflects publicly available information as of July 2026; relevant APIs may evolve.