Skip to main content

Setting Up: Anthropic (Claude) & OpenAI SDKs

Introduction

Theory's done — now you build. Last lesson dissected the anatomy of an API call (messages, roles, params). This one gets you from zero to a working call against real frontier models — both Claude (Anthropic) and GPT (OpenAI) — in a few minutes.

We'll use both providers on purpose. They share the same mental model (you learned it last lesson), but differ in small, practical ways that trip people up constantly. Seeing them side by side now means you'll never be locked into one vendor — a core AI-engineering skill.

You'll learn:

  • How to get API keys from both providers
  • How to store keys safely (the #1 beginner security mistake)
  • Installing the SDKs and making your first call to each
  • The key differences between the Anthropic and OpenAI SDKs
  • A first taste of streaming and staying provider-agnostic

Step 1 — Get Your API Keys

An API key is a secret string that authenticates your requests and ties usage to your billing. Get one from each provider's console:

Two things to know:

  1. You see the key only once. Copy it immediately; if you lose it, revoke and make a new one.
  2. APIs are pay-as-you-go, separate from any chat subscription (ChatGPT Plus / Claude Pro do not include API credits). You'll add a small amount of credit or a card. (We cover cost math in the pricing lesson.)

Step 2 — Store Keys Safely (Do This Right)

The single most common beginner mistake is pasting an API key directly into code — and then committing it to GitHub, where bots scrape and abuse it within minutes. Never hardcode a key.

The standard pattern: keep keys in environment variables, loaded from a .env file that is git-ignored.

# .env  (NEVER commit this file)
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
# .gitignore
.env
# load .env into environment variables
from dotenv import load_dotenv
load_dotenv()   # now os.environ has your keys

Both SDKs automatically read their key from the environment (ANTHROPIC_API_KEY, OPENAI_API_KEY), so once .env is loaded you never touch the key in code. If a key ever leaks, revoke it immediately in the console and issue a new one.

Step 3 — Install the SDKs

Install the official Python libraries (plus python-dotenv for the .env loading above):

pip install anthropic openai python-dotenv

The official SDKs are the right default: they handle auth, retries, errors, and streaming for you. (JavaScript/TypeScript users: npm install @anthropic-ai/sdk openai dotenv — the shapes mirror Python.)

Your First Claude Call (Anthropic SDK)

from dotenv import load_dotenv
from anthropic import Anthropic

load_dotenv()
client = Anthropic()              # reads ANTHROPIC_API_KEY from env

resp = client.messages.create(
    model="claude-opus-4-8",     # or a Sonnet model for cheaper/faster
    max_tokens=1024,              # REQUIRED on Anthropic
    system="You are a concise assistant.",   # system is a TOP-LEVEL param
    messages=[
        {"role": "user", "content": "Say hello in one sentence."},
    ],
)

print(resp.content[0].text)       # response text lives here

Three Anthropic specifics to remember: system is its own parameter (not a message), max_tokens is required, and the reply is at resp.content[0].text.

Your First GPT Call (OpenAI SDK)

OpenAI offers two APIs. The familiar Chat Completions:

from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()
client = OpenAI()                 # reads OPENAI_API_KEY from env

resp = client.chat.completions.create(
    model="gpt-5.5",             # or gpt-5.4-mini for cheaper/faster
    messages=[
        {"role": "system", "content": "You are a concise assistant."},  # system is a MESSAGE
        {"role": "user", "content": "Say hello in one sentence."},
    ],
)

print(resp.choices[0].message.content)   # response text lives here

OpenAI now also recommends the newer Responses API, which simplifies inputs and is built for tools/state:

resp = client.responses.create(
    model="gpt-5.5",
    instructions="You are a concise assistant.",   # the 'system' equivalent
    input="Say hello in one sentence.",
)
print(resp.output_text)

Either works; Chat Completions is the most widely-used and portable, the Responses API is the forward-looking default for richer apps.

A side-by-side code comparison titled 'Same Call, Two Dialects: Anthropic vs OpenAI'. The left card shows the Anthropic SDK: client.messages.create with model claude-opus-4-8, max_tokens=1024 marked REQUIRED, system as a top-level parameter, and messages list; the response is read from resp.content[0].text. The right card shows the OpenAI SDK: client.chat.completions.create with model gpt-5.5, system passed as a message with role system, and a user message; the response is read from resp.choices[0].message.content. Three difference callouts are highlighted: where the system prompt goes (top-level param vs a message), whether max_tokens is required (yes for Anthropic, optional for OpenAI), and how to read the response (resp.content[0].text vs resp.choices[0].message.content). A bottom banner notes the mental model is identical and to mind those three differences.

Same Idea, Two Dialects

Both calls are the same stateless request from last lesson — a model name, a system instruction, and a list of role-tagged messages — but the dialects differ in three spots worth memorizing:

Anthropic (Claude)OpenAI (GPT)
System prompttop-level system= parama message with role: "system"
max_tokensrequiredoptional (max_completion_tokens)
Read the replyresp.content[0].textresp.choices[0].message.content
Callclient.messages.create(...)client.chat.completions.create(...)

Forget max_tokens on Anthropic and the call errors; reach for resp.content on OpenAI and you'll get nothing. Internalize these three and switching providers becomes trivial.

A Taste of Streaming

By default you wait for the whole response. For a chat-like, responsive feel, stream the tokens as they're generated — both SDKs support it:

# Anthropic
with client.messages.stream(
    model="claude-opus-4-8", max_tokens=1024,
    messages=[{"role": "user", "content": "Count to five."}],
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)
# OpenAI (Chat Completions)
stream = client.chat.completions.create(
    model="gpt-5.5",
    messages=[{"role": "user", "content": "Count to five."}],
    stream=True,
)
for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="", flush=True)

Streaming doesn't make generation faster — it just shows tokens as they arrive, which feels dramatically snappier (it's why chatbots type at you). We'll use it in the CLI chatbot at the end of this section.

Staying Provider-Agnostic

Models improve and prices shift constantly, so good AI engineers avoid hard-coupling to one provider. Two simple habits:

  • Put the model name (and provider) in config, not scattered through your code, so swapping is a one-line change.
  • Consider a thin abstraction. Libraries like LiteLLM (or your own small wrapper) expose one interface across providers, so the same call works against Claude, GPT, or a local model — handy for comparing quality/cost or avoiding lock-in.

You don't need this on day one, but design with it in mind: the provider should be a detail you can change, not a foundation you're stuck on.

🧪 Try It Yourself

Your turn — first real calls. With your keys in a .env, make the same request to both Claude and GPT (the snippets above are copy-paste ready). As you do, catch the three dialect differences live: where system goes, whether max_tokens is required, and how you read the response (resp.content[0].text vs resp.choices[0].message.content). Get these in your fingers once and provider-switching becomes trivial.

Common Pitfalls

  • Committing your key. Use .env + .gitignore. If you leak one, revoke it immediately — leaked keys get abused within minutes.
  • Forgetting max_tokens on Anthropic. It's required; the call fails without it.
  • Wrong response path. resp.content[0].text (Anthropic) vs resp.choices[0].message.content (OpenAI) — mixing them up is the classic first bug.
  • Model-name typos / stale names. Model IDs change; an invalid name returns a 404-style error. Keep them in config and check the docs.
  • Expecting your chat subscription to cover the API. It doesn't — API billing is separate.
  • Not handling rate limits. Under load you'll hit 429s; the SDKs retry some, but plan backoff for production (later lessons).

Key Takeaways

  • Get keys from console.anthropic.com and platform.openai.com; API billing is separate from chat subscriptions.
  • Never hardcode keys — use .env + .gitignore + python-dotenv; the SDKs read the key from the environment automatically.
  • Install with pip install anthropic openai python-dotenv; call client.messages.create(...) (Anthropic) or client.chat.completions.create(...) / client.responses.create(...) (OpenAI).
  • Three dialect differences: where the system prompt goes, whether max_tokens is required, and how you read the response.
  • Stream for responsive UX; keep the provider/model in config to stay swappable.

Next: not every model lives behind an API — we'll run open models locally with Ollama, so you can develop offline, for free, and with full privacy.