# Get an API key, make one request

Web page: https://academy.memberpress.ai/courses/api-fundamentals/l1/

Lesson 1 of 9 in [Anthropic API Fundamentals](https://academy.memberpress.ai/courses/api-fundamentals/), 3 min.

Video: https://www.youtube.com/watch?v=ZYPKY1IzVRw

Documentation tells you what a request looks like. Running one tells you what it feels like — the latency, the response shape, what `stop_reason` actually means when you read it live.

**Get your key**

Go to `console.anthropic.com`, sign up, and create an API key under the API Keys section. The console shows the key exactly once — copy it immediately. Store it in an environment variable, not in your source code.

```bash
export ANTHROPIC_API_KEY="sk-ant-..."
```

Add that line to your shell profile (`~/.zshrc` or `~/.bashrc`) so it survives a new terminal session. Every example in this course reads from that variable. Nothing in the codebase ever holds the key string itself — not a config file, not a comment, not a test fixture.

**Make the call**

```bash
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-5",
    "max_tokens": 256,
    "messages": [
      {"role": "user", "content": "Say hello."}
    ]
  }'
```

Three headers, one JSON body. The `anthropic-version` header is required — without it the API returns a `400`. The version string pins the response contract; Anthropic does not break old versions silently, so you can leave this value fixed until you deliberately upgrade.

**Read the response**

```json
{
  "id": "msg_01XFDUDYJgAACzvnptvVoYEL",
  "type": "message",
  "role": "assistant",
  "content": [{"type": "text", "text": "Hello! How can I help you today?"}],
  "model": "claude-opus-4-5",
  "stop_reason": "end_turn",
  "usage": {"input_tokens": 10, "output_tokens": 12}
}
```

The text lives at `content[0].text`. The `usage` block tells you the exact token cost — `input_tokens` for what you sent, `output_tokens` for the reply. Store both when you log requests in production. The `stop_reason` field tells you why generation stopped: `end_turn` means the model finished naturally; `max_tokens` means you hit the cap and the response was cut short.

**The same call in Python**

Once you have a working curl command you understand what is on the wire. Now install the SDK and verify the two produce the same result:

```python
# scripts/hello.py
import anthropic

client = anthropic.Anthropic()  # reads ANTHROPIC_API_KEY from environment

response = client.messages.create(
    model="claude-opus-4-5",
    max_tokens=256,
    messages=[{"role": "user", "content": "Say hello."}],
)

print(response.content[0].text)
print(f"stop_reason: {response.stop_reason}")
print(f"input_tokens: {response.usage.input_tokens}")
print(f"output_tokens: {response.usage.output_tokens}")
```

Run `pip install anthropic` first. The SDK reads `ANTHROPIC_API_KEY` automatically — you do not pass it to the constructor. The `response` object gives you the same fields as the JSON above, typed. `response.content[0].text` is the string. `response.usage.input_tokens` is an integer. The SDK is the curl call with serialization handled.

**Why no library first**

The SDK wraps these three headers and the JSON serialization. Starting with raw curl means you know exactly what is on the wire, which means you can debug any SDK problem by stepping down to curl. That is worth ten minutes now.

**If you get stuck**

- `401 authentication_error`: the key is wrong, missing, or the shell variable was not exported. Run `echo $ANTHROPIC_API_KEY` to confirm it is set. If it prints nothing, you need `export`, not just assignment (`ANTHROPIC_API_KEY=...` without `export` sets a local shell variable that child processes cannot see).
- `400 invalid_request_error`: the JSON is malformed or a required field is missing. Paste the body into `jq .` to check syntax. Confirm `model`, `max_tokens`, and `messages` are all present.
- `400` with a message about `anthropic-version`: you omitted the version header. Add `-H "anthropic-version: 2023-06-01"`.
- Response truncated mid-sentence: `max_tokens` is too low. Raise it and re-run.
- Python `ModuleNotFoundError`: run `pip install anthropic`. If you are in a virtual environment, confirm it is activated before installing.

**Cross-reference**

l2 (Messages, system, and the request shape) breaks down each field in the request body in detail. l4 (Errors, retries, and timeouts) covers what to do when the API returns `429` or `529` in production.

**Deliverable**

A `200` response with `"role": "assistant"` and a non-empty `content[0].text` from both the curl command and the Python script. Note the `input_tokens` value from both runs — they should match.

**Further reading**

- ["One File for All My Keys"](https://sethshoultes.com/blog/canonical-secrets.html) — names the convention that keeps `ANTHROPIC_API_KEY` out of every project's `.env` and into a single canonical file you source.

## Try these prompts

- Write the curl command to call the Anthropic API and ask Claude to summarize a README file in two sentences.
- What does this API response field mean: "stop_reason": "max_tokens"? When would you see it instead of "end_turn"?
- Write the Python snippet that reads ANTHROPIC_API_KEY from the environment and makes a single messages.create call.

## Transcript

Hi, I'm Opal. In this course, I'll teach you to work with the Anthropic API, from your very first request to a real feature in an app. Today, you'll get an API key, send Claude one message, and learn to read what comes back. Here's why I start this way.

Documentation tells you what a request looks like. Sending one tells you what it feels like: how long it takes, what the answer looks like, and what each part of it means. That's worth ten minutes before you write any real code. First, the key.

Sign up in the Anthropic console and create an API key. It's shown to you exactly once, so copy it straight away. Then put it in an environment variable called ANTHROPIC underscore API underscore KEY, and add that line to your shell profile so it's there in every new terminal. The key never goes in your code.

Not in a config file, not in a comment, not in a test. Now the request. I want you to send the first one with curl, before any library. The command is on the lesson page.

It has three headers and a small body. The headers carry your key, the API version, and the content type. The body names the model, sets a limit on how long the reply can be, and holds one message from you: say hello. Starting with curl means you know exactly what's being sent.

If a library ever misbehaves later, you can drop back to curl and see what's really going on. Then read the answer. The reply text sits inside a field called content. There's a usage section, which tells you how many tokens you sent and how many came back.

That's your cost, so log both in production. And there's a stop reason. End turn means Claude finished on its own. Max tokens means it hit your limit and got cut off.

After that, run the same request with the Python library. It reads your key from the environment by itself, and gives you back the same fields. Your token counts in, from curl and from Python, should match. Now the common mistakes.

An authentication error usually means the key isn't set the way you think. Echo the variable. If it prints nothing, you set it without export, so your programs can't see it. A four hundred error that mentions the version means the version header is missing.

And if the reply stops mid-sentence, your token limit is too low, so raise it and run it again. Here's the one thing to take away: keep the key out of your code, send your first call by hand, and read every field that comes back.

Next: [Messages, system, and the request shape](https://academy.memberpress.ai/courses/api-fundamentals/l2/)
