How to Use the Claude API: From Key to First Working Call (2026)
Most "getting started" pages for the Claude API stop at a single copy-paste snippet. That is enough to see one response come back, and not enough to ship anything. This guide walks the whole first mile: what you need before the first call, what each part of a request actually does, how to read the response, and the four mistakes that eat most of a beginner's first afternoon.
What you need before the first call
Three things, and no more:
- An API key. Created in the console of whichever provider you are calling through, and shown once. Treat it as a password — it goes in an environment variable, never in front-end code and never in a committed file.
- A base URL. The official endpoint is
https://api.anthropic.com. Any OpenAI-compatible or Anthropic-compatible gateway will publish its own base URL, and switching between them should be a one-line change. See what an OpenAI-compatible API is if you are unsure which shape your client speaks. - A model id. A string such as
claude-sonnet-4-5orclaude-opus-4-5. Model ids are exact — a typo returns anot_found_error, not a fallback.
If you do not have key access yet, the routes and their blockers are covered in Anthropic Claude API access.
The anatomy of one request
Every call to the Messages API is a POST to /v1/messages with three headers and a small JSON body.
The headers:
x-api-key: <your key>— note it is notAuthorization: Bearer. This is the single most common first-attempt error against the official endpoint.anthropic-version: 2023-06-01— a required, pinned date string. It is not your SDK version and it does not change when models change.content-type: application/json
The body, at minimum:
model— the model id string.max_tokens— required, an integer cap on the response length. There is no default. Omitting it is the second most common first-attempt error.messages— an array of turns, each with aroleofuserorassistantand acontentfield.
So the smallest valid body is: {"model": "claude-sonnet-4-5", "max_tokens": 1024, "messages": [{"role": "user", "content": "Hello"}]}
On the command line that is one POST: the target is /v1/messages on your base URL, the three headers above go in as -H flags, and the body goes in as -d — either inline or, more readably, as -d @body.json with the JSON in a file.
In Python with the official SDK, the same call is client.messages.create(model="claude-sonnet-4-5", max_tokens=1024, messages=[{"role": "user", "content": "Hello"}]), where client = anthropic.Anthropic() picks the key up from the ANTHROPIC_API_KEY environment variable.
If you are pointing at a gateway rather than the official endpoint, the same SDK takes a base_url argument — that one argument is the entire migration.
Reading the response
The response is not a bare string. The field you want is content, and it is a list of blocks, each with a type. For a plain text answer there is one block of type text, so the text lives at content[0].text — not at content.
Two other fields matter from day one:
stop_reason—end_turnmeans the model finished on its own.max_tokensmeans you truncated it, and the answer is cut mid-sentence. If your outputs are mysteriously incomplete, check this field before you change the prompt.usage—input_tokensandoutput_tokensfor that call. This is the only honest basis for cost tracking; see Claude API pricing for how those numbers turn into a bill.
Four things that trip up the first afternoon
The system prompt is not a message. In the Messages API, system is a top-level parameter, not an entry in the messages array with role: "system". Sending a system role inside messages returns a validation error. This is the single biggest difference from the OpenAI chat format, and it is what most copy-pasted snippets get wrong.
The API is stateless. There is no conversation id. For a multi-turn exchange you resend the entire history every call — previous user turns and previous assistant turns — and append the new one. Which also means your input token count grows with every turn, and a long conversation costs more per call than a short one.
max_tokens caps output, not context. It is a ceiling on what comes back, not a budget for what you send. Setting it high does not cost more on its own; you pay for what is actually generated.
Long responses need streaming. Non-streaming requests that would take a long time to generate are rejected rather than left hanging. Anything with a large max_tokens should be streamed. The event sequence and how to consume it correctly is covered in Claude API streaming.
When the call fails
Error responses carry a type inside an error object, and the type is what you branch on — not the prose message, which changes.
authentication_error(401) — bad or missing key. Check the header name first;Authorization: Bearersilently looks correct and is not.invalid_request_error(400) — a malformed body. Missingmax_tokensand asystemrole insidemessagesboth land here.not_found_error(404) — usually a mistyped model id.rate_limit_error(429) — you are over a limit. Readretry-afterand back off; the three separate limits and how to work with them are in Anthropic API rate limits.overloaded_error(529) — capacity on their side, not a limit on yours. Retry with backoff.
Retry 429 and 529 with exponential backoff and jitter. Do not retry 400 or 401 — nothing about them improves with time.
Where a gateway fits
Once more than one person or service is calling the API, three needs appear at once: a single place to rotate keys, per-team usage visibility, and the ability to change models without redeploying every client. That is the job an LLM API gateway does, and it is also what a Claude API proxy is for when the constraint is access or payment rather than governance. Both are a base_url change on the client side, which is why it is worth writing that value into config on day one rather than hard-coding the official host.
Frequently asked questions
Do I need a paid plan to call the API? API access is billed separately from any chat subscription. Credits on one do not carry to the other.
Which model should I start with? Start on a mid-tier model, get the call working, then compare quality on your actual prompts. Model choice is cheap to change — it is one string.
Why does my second question ignore the first? Because you did not resend the history. The API is stateless; see the second point above.
Can I call it from the browser? Not with your real key — anything shipped to a browser is public. Put a thin server route in between.
How do I estimate cost before running anything? Use the usage numbers from a few representative calls and multiply out, rather than guessing from character counts.
The short version
Three headers, max_tokens is required, system is a top-level parameter, the text is at content[0].text, and the API remembers nothing between calls. Get those five right and the first working call is about ten minutes of work; everything after that is prompt design, cost control, and error handling.
ROIBest AI serves an OpenAI-compatible and Anthropic-compatible endpoint, so the code above works unchanged apart from the base URL.