From "I've never called an API" to your first working Claude program โ in about ten minutes. Plain-English explanations, copy-paste samples in curl, Python and TypeScript, and the current models, errors and gotchas. No prior REST experience assumed.
Everything here uses the live Claude API at api.anthropic.com. Code samples default to claude-opus-4-8, Anthropic's most capable Opus-tier model.
An API ("Application Programming Interface") is just a way for your code to ask another computer to do something. A REST API does it over the web, using the same plumbing your browser already uses. Five ideas cover almost everything:
You send a request to a web address (an endpoint). The server does the work and sends back a response. With Claude: you send a message, Claude sends back a reply. That's the whole loop.
Every request has a verb. GET means "read something," POST means "send data and make something happen." Talking to Claude is always a POST โ you're sending a message for it to act on.
Headers are metadata that ride along with the request โ like the envelope around a letter. For Claude they carry your API key (who you are), the API version, and the fact that you're sending JSON.
The actual content travels as JSON: a simple text format of "key": value pairs. You send JSON describing your message; Claude replies with JSON containing its answer. Every language can read and write it.
The response comes with a number. 200 = success. 4xx (like 401, 429) = you need to fix something. 5xx = the server had a problem; retry. We list the ones you'll actually hit below.
The server needs to know it's really you (and who to bill). You prove it with an API key โ a long secret string โ sent in a header. Treat it like a password: it never goes in your code or a public repo.
POST to one endpoint, with three headers and a small JSON body. Everything else โ tools, streaming, vision โ is just more fields in that same body.Pick your language with the toggle on each step โ the samples stay in sync. Copy, run, done.
Sign in at console.anthropic.com โ API keys, create a key (it starts with sk-ant-), and copy it. Then set it as an environment variable so your program can read it without the secret ever appearing in your source:
export ANTHROPIC_API_KEY="sk-ant-...your-key..." # add to ~/.zshrc to persist
/v1/messages. This is the whole API โ learn this and the rest is variations.curl https://api.anthropic.com/v1/messages \ -H "x-api-key: $ANTHROPIC_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{ "model": "claude-opus-4-8", "max_tokens": 1024, "messages": [ { "role": "user", "content": "Hello, Claude. In one sentence, what are you?" } ] }'
No install needed โ curl ships with macOS and Linux. This is the rawest form of the call; it shows exactly what every language sends under the hood.
pip install anthropic
import anthropic # Reads ANTHROPIC_API_KEY from the environment automatically. client = anthropic.Anthropic() message = client.messages.create( model="claude-opus-4-8", max_tokens=1024, messages=[ {"role": "user", "content": "Hello, Claude. In one sentence, what are you?"} ], ) print(message.content[0].text)
The official SDK handles auth, JSON, retries and timeouts for you. Run it with python first_call.py.
npm install @anthropic-ai/sdk
import Anthropic from "@anthropic-ai/sdk"; // Reads ANTHROPIC_API_KEY from the environment automatically. const client = new Anthropic(); const message = await client.messages.create({ model: "claude-opus-4-8", max_tokens: 1024, messages: [ { role: "user", content: "Hello, Claude. In one sentence, what are you?" }, ], }); const first = message.content[0]; if (first.type === "text") console.log(first.text);
Works with Node.js and TypeScript (plain JavaScript too โ same SDK). Run with npx tsx first-call.ts.
| Part | What it is |
|---|---|
/v1/messages | The one endpoint you talk to. v1 is the API version path; messages is the "have a conversation" operation. |
x-api-key | Your secret key โ this is how Anthropic knows it's you and what to bill. (The SDKs read it from the environment so you never type it.) |
anthropic-version | Pins the API's behavior to a known date so your code doesn't break when the API evolves. Always 2023-06-01. |
model | Which Claude to use. claude-opus-4-8 is the most capable; see Models for cheaper/faster options. |
max_tokens | The longest reply you'll allow, in tokens (โยพ of a word each). A safety cap โ set it comfortably above what you expect. |
messages | The conversation, as a list. Each entry has a role ("user" or "assistant") and its content. Your first message is always user. |
{
"id": "msg_01XFDUDYJgAACzvnptvVoYEL",
"type": "message",
"role": "assistant",
"model": "claude-opus-4-8",
"content": [
{ "type": "text", "text": "I'm Claude, an AI assistant made by Anthropic." }
],
"stop_reason": "end_turn",
"usage": { "input_tokens": 18, "output_tokens": 12 }
}
| Field | What to do with it |
|---|---|
content | A list of blocks. The text reply is content[0].text. (It's a list because a response can also contain tool calls or thinking โ more on that later.) |
stop_reason | Why Claude stopped. "end_turn" = finished normally โ
. "max_tokens" = ran out of room (raise max_tokens). "tool_use" = it wants to call a tool. Check this before trusting the output. |
usage | Tokens in and out โ this is what you're billed on. Handy for tracking cost (see pricing). |
Samples here are in Python for readability โ the shape is identical in every SDK.
A top-level instruction that sets the role, tone and rules for the whole conversation. It's separate from the user messages and carries the most weight.
client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
system="You are a terse expert. Answer in one sentence, no preamble.",
messages=[{"role":"user", "content":"What is a REST API?"}],
)
Each call is independent โ Claude remembers nothing between requests. To continue a conversation you resend the whole history every time, appending Claude's last reply.
messages = [
{"role":"user", "content":"My name is J."},
{"role":"assistant", "content":"Nice to meet you, J!"},
{"role":"user", "content":"What's my name?"},
]
# Claude only knows "J" because the history is resent.
Instead of waiting for the whole reply, receive it word-by-word โ like watching it type. Use it for chat UIs and any long output (it also avoids timeouts).
with client.messages.stream( model="claude-opus-4-8", max_tokens=1024, messages=[{"role":"user","content":"Write a haiku about APIs."}], ) as stream: for text in stream.text_stream: print(text, end="", flush=True)
One string swaps the brain. Reach for Opus on hard reasoning, Sonnet for the everyday balance, Haiku when speed and price matter most.
model="claude-opus-4-8" # most capable model="claude-sonnet-4-6" # balanced workhorse model="claude-haiku-4-5" # fastest & cheapest
Each of these is just extra fields on the same /v1/messages call. Tap through to Anthropic's official docs when you need them.
Let Claude call your functions โ search a database, hit an API, do math. You describe the tool; Claude decides when to use it. The foundation of agents.
Read the guide โForce the reply into a JSON shape you define, guaranteed to parse. Perfect for extraction and feeding results into other code.
Read the guide โReusing a big chunk of context across calls? Cache it and pay up to ~90% less for those tokens. A one-line change with huge savings.
Read the guide โAdd images or PDFs to a message and ask about them. Same call โ you just include an image or document block alongside your text.
Read the guide โThe full event model behind text_stream โ for building responsive chat interfaces and progress indicators.
Have thousands of requests that aren't time-sensitive? Submit them as a batch for 50% off. Results come back within 24 hours.
Read the guide โThe status code tells you what happened. Here are the ones you'll actually meet, and how to fix each.
| Code | Meaning | Retry? | What to do |
|---|---|---|---|
| 400 | Bad request | no | Something in your JSON is wrong โ a typo, a bad field, messages not starting with user. Read the message in the error; it usually names the field. |
| 401 | Not authenticated | no | Your API key is missing, wrong, or revoked. Check ANTHROPIC_API_KEY is set and current. |
| 403 | Forbidden | no | The key is valid but lacks access to this model or feature. Check your Console permissions/plan. |
| 404 | Not found | no | Almost always a misspelled model ID. Use an exact ID from the table below โ e.g. claude-opus-4-8, not claude-opus-4.8. |
| 429 | Rate limited | yes | You're sending too fast. Wait and retry โ the retry-after header says how long. The SDKs back off and retry automatically. |
| 529 | Overloaded | yes | Anthropic's side is briefly busy. Retry with a short, increasing delay (the SDKs handle this for you). |
429 and 5xx errors with sensible backoff. That's one more reason to use the SDK rather than raw HTTP once you're past your first request.Swap the model string to change the trade-off. Prices are USD per million tokens (input / output). A token is roughly ยพ of a word.
| Model | Model ID | Context | Max output | Input $/M | Output $/M | Best for |
|---|---|---|---|---|---|---|
| Claude Opus 4.8 default | claude-opus-4-8 | 1M | 128K | $5.00 | $25.00 | The hardest reasoning & long agentic work |
| Claude Sonnet 4.6 | claude-sonnet-4-6 | 1M | 64K | $3.00 | $15.00 | The everyday balance of speed & smarts |
| Claude Haiku 4.5 | claude-haiku-4-5 | 200K | 64K | $1.00 | $5.00 | Fast, cheap, high-volume simple tasks |
| Claude Opus 4.7 | claude-opus-4-7 | 1M | 128K | $5.00 | $25.00 | Previous-gen Opus; pin for reproducibility |
| Claude Fable 5 | claude-fable-5 | 1M | 128K | $10.00 | $50.00 | The most demanding, longest-horizon work |
Use the exact ID string โ don't add date suffixes. For the always-current list, query the Models overview or call GET /v1/models.
The small things that trip up everyone the first time.
Read it from ANTHROPIC_API_KEY (the SDKs do this for you). A key in source code, a webpage, or a public repo is a compromised key โ rotate it instantly if it leaks.
Every request is independent. To continue a conversation, resend the full messages history each time. Forgetting this is the #1 "why doesn't it remember?" question.
max_tokens generouslyToo low and the reply is cut off mid-sentence (stop_reason: "max_tokens"). 1024 is fine for a sentence; use 4096+ for real answers and stream anything large.
Read fields like content[0].text from the parsed object. Never pattern-match the raw response text; escaping and formatting will eventually break it.
stop_reason before trusting outputend_turn means done; max_tokens means truncated; tool_use means it wants a tool; refusal means it declined. Branch on it instead of assuming success.
Past your first curl, use anthropic (Python) or @anthropic-ai/sdk (TS/JS). You get retries, streaming helpers, and typed responses for free.
stop_reason, the agentic loop, tool design, structured output โ are exactly what Domains 1, 3 and 4 test. Drill them in the practice quiz and ask the AI coach anything.