
How to Use the Claude API: First Call in cURL, Python, TS
The Claude API is Anthropic's REST API at api.anthropic.com. Get a key in the Console, store it as ANTHROPIC_API_KEY, then POST to /v1/messages with three headers (x-api-key, anthropic-version, content-type) sending model, max_tokens, and messages. The official Python and TypeScript SDKs wrap this and handle headers, retries, and streaming.
What the Claude API is, and when to use it instead of claude.ai
The Claude API is Anthropic’s REST API for sending prompts to Claude models programmatically, with base URL https://api.anthropic.com. You get a key in the Console, store it as ANTHROPIC_API_KEY, then POST to /v1/messages with a model, max_tokens, and your messages. Official Python and TypeScript SDKs wrap that exact call for you.
Every request needs three headers (x-api-key, anthropic-version, content-type) and a JSON body holding model, max_tokens, and messages. Use claude.ai or the Console Workbench when a human is in the loop typing prompts. Use the API when code needs to call Claude: a backend that classifies support tickets, a script that extracts fields from invoices, an AI agent that runs a tool loop. The dividing line is automation. If a program decides when to call and what to do with the answer, you want the API.
Get an API key in the Console and store it as an environment variable
Go to the Claude Console, open Account Settings, then API Keys, and create a key. Try a couple of prompts in the browser Workbench first so you know the model behaves before you wire it into code.
Then store the key as an environment variable. Never hard-code it, never commit it to git.
export ANTHROPIC_API_KEY="sk-ant-your-key-here"
Put that line in a .env file or your shell profile, and add .env to .gitignore. Both SDKs read ANTHROPIC_API_KEY automatically, so you never pass the key in source code.
- Create a keyConsole, Account Settings, API Keys
- Store itexport ANTHROPIC_API_KEY in your env
- POST /v1/messagesthree headers plus a JSON body
- Read the responsecontent blocks, stop_reason, usage
Your first Claude API call: the raw curl request
Before reaching for an SDK, send the request by hand so you understand what the SDK does under the hood. There is one endpoint, three required headers, and a small JSON body.
curl https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-sonnet-5",
"max_tokens": 1024,
"messages": [
{ "role": "user", "content": "In one sentence, what is the Claude API?" }
]
}'
The three headers are non-negotiable on every request: x-api-key carries your key, anthropic-version pins the API version to 2023-06-01 so your code does not break when the API evolves, and content-type is application/json. The body needs model, max_tokens (a hard cap on output tokens, required), and a messages array. max_tokens is a ceiling, not a target, so size it for your longest expected answer.
The same call in Python and TypeScript
The SDKs send the identical request. They just manage the headers, parse JSON, retry on transient errors, and expose streaming helpers. Install and call.
pip install anthropic
import anthropic
client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY from the env
message = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
messages=[
{"role": "user", "content": "In one sentence, what is the Claude API?"}
],
)
print(message.content[0].text)
TypeScript is the same shape:
npm install @anthropic-ai/sdk
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic(); // reads ANTHROPIC_API_KEY from the env
const message = await client.messages.create({
model: "claude-sonnet-5",
max_tokens: 1024,
messages: [
{ role: "user", content: "In one sentence, what is the Claude API?" },
],
});
console.log(message.content[0].type === "text" && message.content[0].text);
Official SDKs also exist for C#, Go, Java, PHP, and Ruby. They all wrap the same /v1/messages endpoint, so once you understand the curl call you understand all of them. See the Anthropic quickstart for the full per-language setup.
Reading the response: content blocks, stop_reason, and usage
The response is not a bare string. content is an array of typed blocks, because a single reply can mix text, tool calls, and thinking. For a plain text answer you read content[0].text.
{
"id": "msg_013mHbppMPd2PrVJzGMZPt2D",
"type": "message",
"role": "assistant",
"model": "claude-sonnet-5",
"content": [
{ "type": "text", "text": "The Claude API is Anthropic's REST API for sending messages to Claude models." }
],
"stop_reason": "end_turn",
"usage": { "input_tokens": 21, "output_tokens": 17 }
}
Three fields matter in production. content is the array of blocks you iterate over. stop_reason tells you why generation stopped: end_turn means Claude finished, max_tokens means it hit your output cap and was cut off, tool_use means it wants you to run a tool. usage reports input_tokens and output_tokens, which is exactly what you multiply by the per-token price to compute cost. Always check stop_reason. If you see max_tokens when you expected a complete answer, raise max_tokens.
Picking a model: Sonnet 5 vs Haiku 4.5 vs Opus 4.8 and real prices
Three models cover almost every need. Prices below are USD per million tokens, input / output, from the pricing docs.
- Claude Sonnet 5 (
claude-sonnet-5): the sensible default, best combination of speed and intelligence. $3 / $15 standard, with introductory pricing of $2 / $10 through August 31, 2026. 1M-token context, up to 128k output, adaptive thinking. - Claude Haiku 4.5 (
claude-haiku-4-5): the fastest and cheapest, $1 / $5. 200k context, 64k output. Use it for high-volume, latency-sensitive, or simple work like classification. - Claude Opus 4.8 (
claude-opus-4-8): the most capable for hard reasoning and agentic work, $5 / $25. 1M context, 128k output.
Start on Sonnet 5. Drop to Haiku where the task is simple and you run it a lot. Reach for Opus only when Sonnet visibly struggles on the hard cases.
System prompts and multi-turn messages
The system parameter sets role and rules; it is separate from messages. Multi-turn conversations are stateless: the API keeps no memory, so you resend the full history each turn, appending each assistant reply before the next user turn.
message = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
system="You are a terse Mexican fintech engineer. Reply in Spanish, code in English.",
messages=[
{"role": "user", "content": "¿Cómo cobro con Stripe en México?"},
{"role": "assistant", "content": "Usas Stripe Checkout con MXN..."},
{"role": "user", "content": "¿Y cómo emito el CFDI después?"},
],
)
Roles must alternate user, assistant, user. The whole array counts as input tokens every turn, which is why long conversations get expensive and why prompt caching (below) matters.
Streaming long outputs with Server-Sent Events
For long answers, set stream: true. The API returns Server-Sent Events, and you render text as it arrives instead of waiting for the full response. The SDKs expose a helper that hides the event parsing.
with client.messages.stream(
model="claude-sonnet-5",
max_tokens=2048,
messages=[{"role": "user", "content": "Explain prompt caching in detail."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
Streaming does not change the price, only perceived latency. For production patterns around streaming, retries, and error handling, see the deep dive on Sonnet 5 in production.
A minimal tool-use loop
Tool use lets Claude call your code: you describe tools with a JSON schema, Claude returns a tool_use block when it wants one, you run it, and you send the result back as a tool_result. You loop until stop_reason is end_turn.
tools = [{
"name": "get_weather",
"description": "Get current weather for a city.",
"input_schema": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
}]
messages = [{"role": "user", "content": "What's the weather in Monterrey?"}]
resp = client.messages.create(
model="claude-sonnet-5", max_tokens=1024, tools=tools, messages=messages
)
while resp.stop_reason == "tool_use":
block = next(b for b in resp.content if b.type == "tool_use")
result = str(get_weather(block.input["city"])) # your real function
messages.append({"role": "assistant", "content": resp.content})
messages.append({"role": "user", "content": [
{"type": "tool_result", "tool_use_id": block.id, "content": result}
]})
resp = client.messages.create(
model="claude-sonnet-5", max_tokens=1024, tools=tools, messages=messages
)
print(resp.content[0].text)
This loop is the foundation of every agent. To go from this snippet to a real one, read what an AI agent is and the walkthrough on building an autonomous agent with Sonnet 5.
Controlling cost: Batch, prompt caching, and token counting
Three levers cut the bill without changing your model. The Message Batches API (POST /v1/messages/batches) processes large request sets asynchronously at a 50% discount on input and output, ideal for overnight jobs. Prompt caching reuses a stable prefix, like a long system prompt or document, so cached reads cost roughly 0.1x of normal input. Token Counting (POST /v1/messages/count_tokens) estimates a request’s input tokens before you send it, so you can budget.
A worked example. Say you process 200,000 documents on Sonnet 5 at introductory pricing ($2 / $10), each request 4,000 input and 500 output tokens. Standard cost is 200000 × (4000 × $2 + 500 × $10) / 1,000,000 = $2,600. Run the same workload through the Batch API and it halves to $1,300. Add caching on a shared 3,000-token instruction prefix and most of that input drops to 0.1x, taking you well under $1,000. Same model, same output, a third of the cost.
For the deep version of these two levers, see prompt caching to cut token costs and tuning Sonnet 5 on cost, quality, effort, and batch.
Security and next steps with the Claude API
Keep the key in an environment variable or a secrets manager, never in source or client-side code. A leaked key bills your account, so put API calls behind your own backend and rotate keys in the Console if one escapes. Set max_tokens deliberately and watch usage so a runaway prompt cannot quietly run up cost.
You now have the whole getting-started surface: the raw call, both SDKs, the response shape, model choice, system prompts, streaming, a tool loop, and cost control. From here, go deeper into what changed in Sonnet 5 and how to switch, reliable structured outputs in production, or the full AI agents hub.