How to Use the Claude AI API: A Working Code Walkthrough
Using the Claude AI API comes down to five things: getting a key, sending a POST request with your prompt, reading the JSON response, handling multi-turn conversations, and (eventually) adding streaming or tool use. This article walks through each step with working code so you can go from zero to a functioning integration in under an hour.
Everything below applies whether you're calling Anthropic's API directly or going through a proxy like SubToAPI that wraps Claude access in a standard HTTPS API — the request/response shape is the same either way, which is the point of this guide.
Step 1: Get an API Key
You need an API key before anything else. If you're using Anthropic directly, you get one from their console after setting up billing. If you already pay for Claude (Pro, Max, or a team plan) and don't want to manage a separate Anthropic billing account, a service like SubToAPI turns that access into an API key you can use immediately — sign up at /signup and you get a key in the sub_live_... format along with a short free trial.
Whichever route you take, treat the key like a password: store it in an environment variable, never commit it to git, never expose it in frontend JavaScript.
export SUBTOAPI_KEY="sub_live_xxxxxxxxxxxx"
Step 2: Send Your First Request
The core of the API is a single endpoint that accepts a list of messages and returns a completion. Here's the minimal call:
curl https://api.subtoapi.app/v1/messages \
-H "Authorization: Bearer $SUBTOAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "Explain what a race condition is in two sentences."}
]
}'
You'll get back a JSON object with the model's reply, a stop reason, and token usage:
{
"id": "msg_01Xyz",
"role": "assistant",
"content": [
{"type": "text", "text": "A race condition occurs when..."}
],
"stop_reason": "end_turn",
"usage": {"input_tokens": 14, "output_tokens": 42}
}
That's the entire loop: send messages, get content back. Full request/response fields are documented at /docs/messages, and a quickstart with more examples lives at /docs/quickstart.
Step 3: Use a System Prompt to Set Behavior
Most real applications need the model to follow consistent instructions across every request — a persona, output format, or constraints. That goes in a separate system field, not mixed into the conversation:
curl https://api.subtoapi.app/v1/messages \
-H "Authorization: Bearer $SUBTOAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4",
"max_tokens": 500,
"system": "You are a terse code reviewer. Only point out bugs and security issues, no style comments.",
"messages": [
{"role": "user", "content": "def add(a, b): return a+b"}
]
}'
Keeping instructions in system rather than the first user message makes the model far more reliable at following them, and it's easier to swap personas without rewriting conversation history.
Step 4: Handle Multi-Turn Conversations
Claude's API is stateless — it doesn't remember previous calls. To build a conversation, you resend the full message history each time, alternating user and assistant roles:
const messages = [
{ role: "user", content: "What's a good name for a task tracker app?" },
{ role: "assistant", content: "How about 'Focusly'?" },
{ role: "user", content: "Give me two more options in that style." }
];
const res = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
model: "claude-sonnet-4",
max_tokens: 300,
messages
})
});
const data = await res.json();
console.log(data.content[0].text);
Each new turn: append the model's previous reply as an assistant message, add the user's new message, and send the whole array again. This is how "memory" works in every chat-style LLM API — the client owns the history, not the server.
Step 5: Stream Responses for Better UX
For anything user-facing, waiting for the full response before showing anything feels slow. Streaming sends the reply token-by-token as it's generated, so you can render it live:
const res = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
model: "claude-sonnet-4",
max_tokens: 1024,
stream: true,
messages: [{ role: "user", content: "Write a haiku about deploying on a Friday." }]
})
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
process.stdout.write(decoder.decode(value));
}
The response arrives as a series of server-sent events you parse incrementally. Details on event types and reconnection handling are in /docs/streaming.
Step 6: Let Claude Call Your Functions (Tool Use)
If your app needs Claude to fetch data, run calculations, or trigger actions instead of just generating text, define tools with a JSON schema and pass them in the request. The model decides when to call a tool, returns structured arguments instead of text, and you execute the function and send the result back in a follow-up message. This is how you build agents that look up order status, query a database, or hit a weather API on the user's behalf. Full schema and multi-step examples are at /docs/tools.
Common Mistakes to Avoid
- Not setting
max_tokens— this is required and caps output length; too low truncates responses mid-sentence. - Mixing system instructions into user messages — hurts instruction-following consistency.
- Resending an unbounded history — long conversations get expensive and slow; summarize or trim older turns.
- Ignoring
stop_reason— check whether the response ended naturally (end_turn) or was cut off (max_tokens) before trusting the output is complete. - Not handling rate limit or error responses — always check the HTTP status code, not just
response.ok, and implement retry with backoff for429s.
If you'd rather skip separate Anthropic billing and dashboard management, SubToAPI gives you the same request/response format under one API key, with usage tracking and team seats built in. Plans start at €9/month, and pricing details are at /pricing.
questions
Do I need to manage conversation history myself? Yes. The API has no server-side memory between calls — you resend the full message array (including prior assistant replies) with every request to maintain context.
What's the difference between streaming and non-streaming calls? Non-streaming waits for the full response before returning it as one JSON object. Streaming sends the text incrementally via server-sent events, which is better for chat UIs where you want to show output as it's generated.
Can I use the Claude API without an Anthropic developer account? Yes, if you already have Claude access through a subscription. Services like SubToAPI convert that access into a standard API key so you can make the same kind of requests without setting up separate Anthropic billing.