# Your First Claude API Request: curl, Python, Node

> Get a key, set ANTHROPIC_API_KEY, and make a working Messages API call in curl, Python, and Node — then read the response properly.

- Canonical: https://guides-ai.pages.dev/guides/claude-api-first-request/
- Plate 15.01 · Topic: Building with the API (https://guides-ai.pages.dev/topics/api/)
- Published: 06 Sept 2026 · 3 min read
- Source site: guides-ai — https://guides-ai.pages.dev/

Everything in the Claude API goes through one endpoint: `POST https://api.anthropic.com/v1/messages`.

## 1. Key and environment variable

Create a key in the Claude Console under Settings → API keys, then export it. Never hardcode it in source or commit it:

```bash
export ANTHROPIC_API_KEY="your-api-key-here"
```

That puts the key where every Anthropic SDK looks by default — no `api_key=` argument needed anywhere below.

## 2. curl

```bash
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-opus-5",
    "max_tokens": 1000,
    "messages": [{"role": "user", "content": "Explain HTTP 429 in one sentence."}]
  }'
```

Sends one user turn and returns a JSON message. `anthropic-version` and `max_tokens` are both required — omit either and you get a 400.

## 3. Python

```bash
pip install anthropic
```

```python
import anthropic

client = anthropic.Anthropic()  # reads ANTHROPIC_API_KEY

message = client.messages.create(
    model="claude-opus-5",
    max_tokens=1000,
    messages=[{"role": "user", "content": "Explain HTTP 429 in one sentence."}],
)

for block in message.content:
    if block.type == "text":
        print(block.text)
```

The same call through the SDK, with retries and timeouts handled for you.

## 4. Node

```bash
npm install @anthropic-ai/sdk
```

```javascript
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic(); // reads ANTHROPIC_API_KEY

const message = await client.messages.create({
  model: "claude-opus-5",
  max_tokens: 1000,
  messages: [{ role: "user", content: "Explain HTTP 429 in one sentence." }],
});

const text = message.content.find((b) => b.type === "text");
console.log(text.text);
```

Identical request shape — the SDKs are thin wrappers over the same JSON.

## 5. Read the response

```json
{
  "id": "msg_013mHbppMPd2PrVJzGMZPt2D",
  "model": "claude-opus-5",
  "role": "assistant",
  "content": [{ "type": "text", "text": "..." }],
  "stop_reason": "end_turn",
  "usage": { "input_tokens": 21, "output_tokens": 305 }
}
```

`content` is an **array of blocks** — check `type` before touching `.text`, because a reply can also contain thinking or `tool_use` blocks. `stop_reason: "end_turn"` means Claude finished; `"max_tokens"` means you truncated it.

## Picking a model

`claude-opus-5` is the current general recommendation; `claude-sonnet-5` is cheaper and faster, `claude-haiku-4-5` cheaper still. Model IDs change — take the exact string from the models page rather than copying an old one.

---

Next: [tool use](/guides/claude-api-tool-use/) · [streaming responses](/guides/stream-llm-responses/).
