Claude API Fundamentals
With the Claude API (Claude Developer Platform) you can build Claude into your own app. Everything goes through one main endpoint: POST /v1/messages (the Messages API). Tools, thinking and structured output are all features of this same endpoint.
5.1 Setup
- Create an account on platform.claude.com (Console) and add billing.
- Create an API key (inside a workspace). Do not hardcode the key in code — keep it in the ANTHROPIC_API_KEY environment variable.
- Install the SDK: Python pip install anthropic, TypeScript npm install @anthropic-ai/sdk, C# dotnet add package Anthropic. There are also official SDKs for Java, Go, Ruby and PHP.
- 1.Your app: model, system, messages, tools
- 2.POST /v1/messages: x-api-key + anthropic-version
- 3.Claude model: thinking + generation
- 4.Response: content blocks + stop_reason + usage
The API is stateless: every request must carry the full conversation history
5.2 First request (Python, C#, cURL)
import anthropic
client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY from the environment
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=16000,
system="You are a helpful assistant that answers in Hinglish.",
messages=[{"role": "user", "content": "What is an index in SQL?"}],
)
for block in response.content: # content is a list (text, thinking, tool_use...)
if block.type == "text":
print(block.text)
print(response.stop_reason, response.usage.input_tokens, response.usage.output_tokens)using Anthropic;
using Anthropic.Models.Messages;
AnthropicClient client = new(); // reads ANTHROPIC_API_KEY from the environment
var message = await client.Messages.Create(new MessageCreateParams
{
Model = "claude-opus-5-5",
MaxTokens = 1024,
Messages = [ new UserMessageParam { Content = "Hello, Claude" } ],
});
Console.WriteLine(message);curl https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2026-10-21" \
-d '{"model": "claude-opus-5-5", "max_tokens": 1024,
"messages": [{"role": "user", "content": "Hello, Claude"}]}'5.3 Request parameters
| Parameter | Purpose | Note |
|---|---|---|
| model | Which model | Exact ID, e.g. claude-opus-5-5 |
| max_tokens | Upper limit on output | If set too low the answer is cut off midway (stop_reason: max_tokens) |
| messages | Conversation: user / assistant turns | The first message is the user's; you send the history yourself |
| system | System prompt (role, rules) | A top-level field, not inside messages |
| tools | Functions Claude can call | See section 7 |
| thinking | Thinking mode, e.g. {type: "adaptive"} | Section 6 |
| output_config | effort, structured format | effort goes here, not top-level |
| stream | Send tokens as they arrive (SSE) | Required for long outputs |
| stop_sequences | Stop on custom strings | On a match, stop_reason: stop_sequence |
| metadata | Info such as user_id | For abuse detection |
5.4 Response and stop_reason
{
"id": "msg_01...",
"type": "message",
"role": "assistant",
"model": "claude-opus-5-5",
"content": [{"type": "text", "text": "An index is a data structure..."}],
"stop_reason": "end_turn",
"usage": {"input_tokens": 25, "output_tokens": 180,
"cache_read_input_tokens": 0, "cache_creation_input_tokens": 0}
}| stop_reason | Meaning | What to do |
|---|---|---|
| end_turn | Claude finished its answer naturally | Normal |
| max_tokens | Cut off at the limit | Raise max_tokens or ask it to continue |
| stop_sequence | A custom stop string was hit | The stop_sequence field says which one |
| tool_use | Claude wants to call a tool | Run the tool, send back a tool_result |
| pause_turn | A long server-tool task paused midway | Send the response back and let it continue |
| refusal | The safety classifier declined | Check stop_details.category; you can use a fallback model |
| model_context_window_exceeded | The context window is full | Compaction / context editing |
5.5 Multi-turn conversation
history = []
def chat(user_text):
history.append({"role": "user", "content": user_text})
r = client.messages.create(model="claude-opus-5-5", max_tokens=4000, messages=history)
history.append({"role": "assistant", "content": r.content}) # append the full content back
return "".join(b.text for b in r.content if b.type == "text")
chat("My name is Rahul")
print(chat("What is my name?")) # remembered because of the history5.6 Streaming
In streaming, the response arrives in pieces over Server-Sent Events (SSE), so the user sees text immediately and long requests do not time out. Event order: message_start → content_block_start → content_block_delta (repeatedly) → content_block_stop → message_delta (stop_reason, usage) → message_stop. A ping can also arrive in between.
with client.messages.stream(
model="claude-opus-5-5", max_tokens=64000,
messages=[{"role": "user", "content": "Write a long article on microservices"}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
final = stream.get_final_message() # the complete message object5.7 Errors and rate limits
| HTTP | Type | Meaning / Fix |
|---|---|---|
| 400 | invalid_request_error | Bad request (param, format). Do not retry, fix it |
| 401 | authentication_error | API key wrong/missing |
| 403 | permission_error | The key has no permission for that resource |
| 404 | not_found_error | Model/resource not found |
| 413 | request_too_large | Request is over the size limit |
| 429 | rate_limit_error | Limit exceeded; retry after the retry-after header |
| 500 | api_error | Anthropic-side error; retry |
| 529 | overloaded_error | API overloaded; retry with backoff |
- There are three kinds of rate limits: RPM (requests/min), ITPM (input tokens/min), OTPM (output tokens/min). Limits grow as your usage tier goes up.
- The SDK retries 2 times by default (408, 409, 429, 5xx) with exponential backoff. Default timeout is 10 minutes.
- In error handling, catch from specific to general: RateLimitError → APIStatusError → APIConnectionError.