Your First Claude API Call
Get from zero to a working Claude API integration in Python or Node.js. Covers installation, your first call, streaming, error handling, and the parameters that matter.
This tutorial takes you from zero to a working Claude API integration. By the end you will have made your first API call in Python and Node.js, understand the response object, added streaming, and have proper error handling in place.
What You Need
- An Anthropic API key — get one at console.anthropic.com
- Python 3.8+ or Node.js 18+ installed
- Basic familiarity with either language
- 5–10 minutes
Installation
pip install anthropic
npm install @anthropic-ai/sdk # or yarn add @anthropic-ai/sdk
Your First Call in Python
Create a file called first_call.py. Set your API key as an environment variable first.
export ANTHROPIC_API_KEY="your-api-key-here"
import anthropic
# The client automatically reads ANTHROPIC_API_KEY from the environment
client = anthropic.Anthropic()
message = client.messages.create(
model="claude-opus-4-5",
max_tokens=1024,
messages=[
{"role": "user", "content": "Explain what a REST API is in two sentences."}
]
)
print(message.content[0].text)Line-by-Line Explanation
- import anthropic — imports the SDK
- anthropic.Anthropic() — creates a client. If ANTHROPIC_API_KEY is set, it picks it up automatically. You can also pass api_key="..." but do not do this in production.
- model="claude-opus-4-5" — the model to use. claude-opus-4-5 is the most capable; claude-haiku-4-5 is fastest and cheapest for simple tasks.
- max_tokens=1024 — the maximum number of tokens in the response. Always set this to avoid surprise costs.
- messages=[...] — the conversation history. For a single-turn call this is just one user message.
- message.content[0].text — the text of the first content block in the response.
Your First Call in Node.js / TypeScript
import Anthropic from "@anthropic-ai/sdk";
// Client reads ANTHROPIC_API_KEY from process.env automatically
const client = new Anthropic();
async function main() {
const message = await client.messages.create({
model: "claude-opus-4-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: "Explain what a REST API is in two sentences.",
},
],
});
// content is an array of blocks — text blocks have a .text property
const textBlock = message.content[0];
if (textBlock.type === "text") {
console.log(textBlock.text);
}
}
main();Understanding the Response Object
The response from client.messages.create() contains more than just the text. Here is the full structure with comments:
{
"id": "msg_01XFDUDYJgAACzvnptvVoYEL", // unique message ID
"type": "message",
"role": "assistant",
"model": "claude-opus-4-5", // model that was used
"content": [
{
"type": "text", // content block type
"text": "A REST API is..." // the actual response text
}
],
"stop_reason": "end_turn", // why generation stopped
// "end_turn" = natural end
// "max_tokens" = hit your max_tokens limit — increase it if you see this
// "stop_sequence" = hit a stop sequence you defined
"stop_sequence": null,
"usage": {
"input_tokens": 18, // tokens in your messages + system prompt
"output_tokens": 47 // tokens in the response
}
}Adding Streaming
Streaming sends tokens as they are generated rather than waiting for the full response. This dramatically improves perceived latency for users. It is one line of change in both languages.
import anthropic
client = anthropic.Anthropic()
# Use stream() instead of messages.create()
with client.messages.stream(
model="claude-opus-4-5",
max_tokens=1024,
messages=[
{"role": "user", "content": "Write a short poem about APIs."}
],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True) # print each token as it arrives
print() # newline after streaming completesimport Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
async function main() {
const stream = await client.messages.stream({
model: "claude-opus-4-5",
max_tokens: 1024,
messages: [
{ role: "user", content: "Write a short poem about APIs." },
],
});
// Stream text chunks as they arrive
for await (const chunk of stream) {
if (
chunk.type === "content_block_delta" &&
chunk.delta.type === "text_delta"
) {
process.stdout.write(chunk.delta.text);
}
}
// Get the final complete message after streaming
const finalMessage = await stream.finalMessage();
console.log("
Total tokens used:", finalMessage.usage.output_tokens);
}
main();Handling Errors Properly
API calls fail. Rate limits, network issues, invalid parameters — handle them explicitly. The SDK provides typed error classes for each failure mode.
import anthropic
client = anthropic.Anthropic()
try:
message = client.messages.create(
model="claude-opus-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
)
print(message.content[0].text)
except anthropic.APIConnectionError as e:
print("Network error — check your internet connection")
print(e)
except anthropic.RateLimitError as e:
# Implement exponential backoff here in production
print("Rate limit hit — slow down your requests")
print(e)
except anthropic.AuthenticationError as e:
print("Invalid API key — check ANTHROPIC_API_KEY")
print(e)
except anthropic.APIStatusError as e:
# Catch all other API errors (400, 500, etc.)
print(f"API error: {e.status_code}")
print(e.message)import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
async function callClaude(prompt: string): Promise<string> {
try {
const message = await client.messages.create({
model: "claude-opus-4-5",
max_tokens: 1024,
messages: [{ role: "user", content: prompt }],
});
const textBlock = message.content[0];
return textBlock.type === "text" ? textBlock.text : "";
} catch (error) {
if (error instanceof Anthropic.APIConnectionError) {
throw new Error("Network error — check your connection");
}
if (error instanceof Anthropic.RateLimitError) {
throw new Error("Rate limit exceeded — implement backoff");
}
if (error instanceof Anthropic.AuthenticationError) {
throw new Error("Invalid API key");
}
throw error; // re-throw unexpected errors
}
}Setting max_tokens and temperature
message = client.messages.create(
model="claude-opus-4-5",
max_tokens=2048, # max tokens to generate — always set this
temperature=0.7, # 0.0 = deterministic/factual, 1.0 = creative/varied
system="You are a helpful assistant.", # optional system prompt
messages=[
{"role": "user", "content": "Write a product description for noise-cancelling headphones."}
],
stop_sequences=["---"], # stop generation when this string appears (optional)
)
# temperature guidance:
# 0.0 — best for: classification, data extraction, factual Q&A
# 0.3 — best for: code generation, technical explanations
# 0.7 — best for: general writing, summaries
# 1.0 — best for: creative writing, brainstormingMulti-Turn Conversations
The Claude API is stateless — there are no sessions. To have a multi-turn conversation, you pass the full conversation history in every request.
import anthropic
client = anthropic.Anthropic()
# Build conversation history manually
conversation = []
def chat(user_message: str) -> str:
# Add user message to history
conversation.append({"role": "user", "content": user_message})
# Send the full history every time
response = client.messages.create(
model="claude-opus-4-5",
max_tokens=1024,
system="You are a helpful programming tutor.",
messages=conversation
)
assistant_message = response.content[0].text
# Add assistant response to history for next turn
conversation.append({"role": "assistant", "content": assistant_message})
return assistant_message
# Example multi-turn session
print(chat("What is a closure in JavaScript?"))
print(chat("Can you give me a practical example?"))
print(chat("How is this different from a regular function?"))What to Build Next
- A command-line tool that takes a filename and reviews the code in it
- A batch processor that reads a CSV of questions and writes Claude's answers to a new column
- A Slack bot that routes questions to Claude and posts the reply back to the channel
- A document summariser that takes a URL, fetches the content, and returns a summary
- A structured data extractor — give it freeform text, get back clean JSON
- A multi-model router that sends creative tasks to a high-temperature model and factual tasks to a low-temperature one
Ready to go further?
Take the interactive course — daily lessons, real exercises, XP and streaks. Turn reading into lasting skills.
