OpenAI vs Anthropic SDK Setup 2026
Two SDKs, One Decision
Every developer building an AI-powered application in 2026 faces the same early question: OpenAI or Anthropic? Both companies ship well-maintained TypeScript and Python SDKs. Both support streaming, function calling, and vision. Both have generous free tiers for development.
The differences show up in API ergonomics, streaming boilerplate, tool use syntax, and pricing at scale. This guide walks through both SDKs side by side — installation, authentication, basic completions, streaming, error handling, and function calling — with pricing data to inform your cost modeling.
TL;DR
OpenAI's SDK (openai) is the default choice if you need the broadest model selection, image generation (DALL-E 3), text-to-speech (TTS), Whisper speech-to-text, or Assistants API with persistent threads. The Node.js SDK is mature, widely documented, and has the largest ecosystem of integrations and examples.
Anthropic's SDK (@anthropic-ai/sdk) has cleaner streaming ergonomics, a more readable tool use syntax, and a model family (Claude 3.x/4.x) that leads benchmarks on long-context reasoning and instruction-following. For pure text generation and agent tasks, Anthropic's Claude models are competitive with GPT-4o on most tasks and outperform on specific benchmarks.
Pricing: At comparable capability tiers, the cost difference is small. GPT-4o and Claude Sonnet 4.6 are within 20% of each other on input tokens. The output token cost difference is more significant for generation-heavy workloads.
Installation and Authentication
Both SDKs are installed via npm or pip with minimal dependencies.
Node.js
# OpenAI
npm install openai
# Anthropic
npm install @anthropic-ai/sdk
// OpenAI
import OpenAI from 'openai'
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
})
// Anthropic
import Anthropic from '@anthropic-ai/sdk'
const anthropic = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
})
Python
# OpenAI
pip install openai
# Anthropic
pip install anthropic
# OpenAI
from openai import OpenAI
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
# Anthropic
import anthropic
client = anthropic.Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))
Authentication is equivalent between the SDKs. Both read the API key from constructor arguments or from the corresponding environment variable (OPENAI_API_KEY / ANTHROPIC_API_KEY) if the constructor argument is omitted — a clean default that avoids hardcoded keys.
Basic Completions
The core API call differs in structure. Anthropic requires explicit max_tokens; OpenAI makes it optional (defaulting to model-specific limits).
Node.js Completions
// OpenAI
const completion = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{ role: 'user', content: 'Explain API rate limiting in two sentences.' }
],
})
console.log(completion.choices[0].message.content)
// Anthropic
const message = await anthropic.messages.create({
model: 'claude-sonnet-4-6',
max_tokens: 1024,
messages: [
{ role: 'user', content: 'Explain API rate limiting in two sentences.' }
],
})
console.log(message.content[0].type === 'text' ? message.content[0].text : '')
The Anthropic response structure wraps content in a typed array (content[0].text) rather than the string access pattern in OpenAI (choices[0].message.content). This is more verbose for simple access but makes multi-block responses (mixed text + tool use) unambiguous to parse.
System Prompts
Both SDKs support system prompts but with different placement:
// OpenAI — system is a message role
const completion = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{ role: 'system', content: 'You are a concise technical writer.' },
{ role: 'user', content: 'What is REST?' }
],
})
// Anthropic — system is a top-level parameter
const message = await anthropic.messages.create({
model: 'claude-sonnet-4-6',
max_tokens: 1024,
system: 'You are a concise technical writer.',
messages: [
{ role: 'user', content: 'What is REST?' }
],
})
Anthropic's system-as-top-level-parameter is a minor ergonomic win: it is harder to accidentally append a system message mid-conversation, and it is visually separated from the conversation turn array.
Streaming: Where the APIs Diverge Most
Streaming is where the implementation philosophy differences become most apparent.
OpenAI Streaming (Node.js)
OpenAI supports both the raw createReadableStream pattern and a higher-level stream() helper:
// OpenAI — async iterable pattern
const stream = openai.beta.chat.completions.stream({
model: 'gpt-4o',
messages: [{ role: 'user', content: 'Write a 200-word essay on caching.' }],
})
for await (const chunk of stream) {
const text = chunk.choices[0]?.delta?.content ?? ''
process.stdout.write(text)
}
const final = await stream.finalChatCompletion()
console.log('\nTotal tokens:', final.usage?.total_tokens)
OpenAI also has a lower-level SSE streaming approach:
// OpenAI — low-level SSE
const stream = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [{ role: 'user', content: 'Explain JWT tokens.' }],
stream: true,
})
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? '')
}
Anthropic Streaming (Node.js)
Anthropic's SDK provides a stream() helper that abstracts the SSE events into a clean async interface:
// Anthropic — stream() helper
const stream = anthropic.messages.stream({
model: 'claude-sonnet-4-6',
max_tokens: 1024,
messages: [{ role: 'user', content: 'Write a 200-word essay on caching.' }],
})
for await (const text of stream.text_stream) {
process.stdout.write(text)
}
const final = await stream.getFinalMessage()
console.log('Input tokens:', final.usage.input_tokens)
console.log('Output tokens:', final.usage.output_tokens)
The stream.text_stream async iterable yields string chunks directly — no ?.delta?.content ?? '' unwrapping required. For applications that only need text content (the vast majority), this reduces boilerplate by approximately 40% compared to OpenAI's equivalent.
For applications that need to distinguish between text blocks and tool use blocks in a streaming response, Anthropic also exposes the full event stream:
const stream = anthropic.messages.stream({ /* ... */ })
stream.on('text', (text) => process.stdout.write(text))
stream.on('message', (message) => {
console.log('Stop reason:', message.stop_reason)
})
Python Streaming
Both SDKs support Python streaming via async generators:
# OpenAI
with client.chat.completions.stream(
model="gpt-4o",
messages=[{"role": "user", "content": "Explain REST APIs."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
# Anthropic
with client.messages.stream(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "Explain REST APIs."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
Python streaming is nearly identical between the two SDKs — both use context managers and .text_stream iterators. The 40% boilerplate reduction is more pronounced in the Node.js/TypeScript version.
Error Handling
Both SDKs throw typed errors that distinguish between API errors, rate limits, and network failures.
OpenAI Error Handling
import { OpenAI, APIError } from 'openai'
try {
const completion = await openai.chat.completions.create({ /* ... */ })
} catch (err) {
if (err instanceof OpenAI.APIError) {
console.error('Status:', err.status) // 429, 500, etc.
console.error('Message:', err.message)
console.error('Code:', err.code) // 'rate_limit_exceeded'
console.error('Type:', err.type) // 'requests'
if (err.status === 429) {
// Respect Retry-After header
const retryAfter = err.headers['retry-after']
await sleep(parseInt(retryAfter ?? '1') * 1000)
}
}
}
Anthropic Error Handling
import Anthropic from '@anthropic-ai/sdk'
try {
const message = await anthropic.messages.create({ /* ... */ })
} catch (err) {
if (err instanceof Anthropic.APIError) {
console.error('Status:', err.status)
console.error('Message:', err.message)
if (err instanceof Anthropic.RateLimitError) {
// err.headers['retry-after'] is available
await sleep(60_000)
}
if (err instanceof Anthropic.AuthenticationError) {
console.error('Check your ANTHROPIC_API_KEY')
}
}
}
Anthropic's SDK exports specific error subclasses (RateLimitError, AuthenticationError, PermissionDeniedError, NotFoundError, UnprocessableEntityError) which enable precise instanceof checks without inspecting status codes. OpenAI's SDK uses the err.code string for similar differentiation. Both approaches are valid; Anthropic's typed subclasses are slightly more ergonomic for comprehensive error handling.
Function Calling vs Tool Use
This is the most significant API design difference between the two platforms.
OpenAI Function Calling
const completion = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{ role: 'user', content: 'What is the current weather in San Francisco?' }
],
tools: [
{
type: 'function',
function: {
name: 'get_current_weather',
description: 'Get the current weather for a location',
parameters: {
type: 'object',
properties: {
location: {
type: 'string',
description: 'The city and state, e.g. San Francisco, CA',
},
},
required: ['location'],
},
},
},
],
tool_choice: 'auto',
})
const toolCall = completion.choices[0].message.tool_calls?.[0]
if (toolCall?.function.name === 'get_current_weather') {
const args = JSON.parse(toolCall.function.arguments)
const weatherResult = await fetchWeather(args.location)
// Second request with tool result
const final = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{ role: 'user', content: 'What is the current weather in San Francisco?' },
completion.choices[0].message,
{
role: 'tool',
tool_call_id: toolCall.id,
content: JSON.stringify(weatherResult),
},
],
tools: [...],
})
}
Anthropic Tool Use
const message = await anthropic.messages.create({
model: 'claude-sonnet-4-6',
max_tokens: 1024,
tools: [
{
name: 'get_current_weather',
description: 'Get the current weather for a location',
input_schema: {
type: 'object',
properties: {
location: {
type: 'string',
description: 'The city and state, e.g. San Francisco, CA',
},
},
required: ['location'],
},
},
],
messages: [
{ role: 'user', content: 'What is the current weather in San Francisco?' }
],
})
const toolUse = message.content.find(block => block.type === 'tool_use')
if (toolUse && toolUse.type === 'tool_use') {
const weatherResult = await fetchWeather(toolUse.input.location)
// Second request with tool result
const final = await anthropic.messages.create({
model: 'claude-sonnet-4-6',
max_tokens: 1024,
tools: [...],
messages: [
{ role: 'user', content: 'What is the current weather in San Francisco?' },
{ role: 'assistant', content: message.content },
{
role: 'user',
content: [
{
type: 'tool_result',
tool_use_id: toolUse.id,
content: JSON.stringify(weatherResult),
},
],
},
],
})
}
Key differences in tool use:
- Schema key: OpenAI uses
parameters(JSON Schema); Anthropic usesinput_schema(also JSON Schema but different key name) - Tool call location: OpenAI puts tool calls in
choices[0].message.tool_calls; Anthropic puts them in the top-levelcontentarray alongside text blocks - Tool result format: OpenAI uses a
toolrole message; Anthropic uses ausermessage withtool_resulttyped content block tool_choice: OpenAI uses'auto','none', or{ type: 'function', function: { name: '...' } }; Anthropic uses'auto','none','any', or{ type: 'tool', name: '...' }
For complex multi-tool agents, Anthropic's content-block model is easier to reason about when a response contains both text and tool calls. OpenAI's model works well for single-tool scenarios.
Both APIs are compatible with the Model Context Protocol (MCP) for standardized tool definitions — see the REST vs GraphQL vs gRPC guide for how protocol choices interact with AI tool architectures.
Pricing Comparison: Q1 2026
Token pricing determines the economics of production AI applications. Costs here are per million tokens.
Standard Models
| Model | Input $/M | Output $/M | Context Window |
|---|---|---|---|
| GPT-4o (OpenAI) | $2.50 | $10.00 | 128K |
| GPT-4o mini (OpenAI) | $0.15 | $0.60 | 128K |
| Claude Sonnet 4.6 (Anthropic) | $3.00 | $15.00 | 200K |
| Claude Haiku 4.5 (Anthropic) | $0.80 | $4.00 | 200K |
| Claude Opus 4.6 (Anthropic) | $15.00 | $75.00 | 200K |
Cost Modeling by Use Case
Chatbot (mostly input, some output): Typical pattern: 500 input tokens, 200 output tokens per message.
- GPT-4o: 500 × $2.50/M + 200 × $10/M = $0.00125 + $0.002 = $0.00325 per message
- Claude Sonnet 4.6: 500 × $3/M + 200 × $15/M = $0.0015 + $0.003 = $0.0045 per message
- GPT-4o costs ~28% less than Sonnet 4.6 for this pattern
Document analysis (large input): Typical pattern: 10,000 input tokens, 500 output tokens.
- GPT-4o: $0.025 + $0.005 = $0.030 per document
- Claude Sonnet 4.6: $0.030 + $0.0075 = $0.0375 per document
- GPT-4o costs ~20% less for long-context analysis workloads
Code generation (balanced): Typical pattern: 2,000 input tokens, 1,500 output tokens.
- GPT-4o: $0.005 + $0.015 = $0.020 per request
- Claude Sonnet 4.6: $0.006 + $0.0225 = $0.0285 per request
- GPT-4o costs ~30% less for balanced code generation
Long-context summarization: Claude's 200K context window (vs GPT-4o's 128K) is a functional advantage for very long documents. If your documents exceed 128K tokens, Claude is the only option at the standard model tier without moving to GPT-4 Turbo.
The pricing gap narrows substantially at the smaller model tier: GPT-4o mini ($0.15/$0.60) vs Claude Haiku 4.5 ($0.80/$4.00). GPT-4o mini is significantly cheaper for high-volume, simple tasks.
Batch Processing Discounts
Both platforms offer batch API pricing at ~50% discount for non-real-time workloads:
- OpenAI Batch API: 50% discount on all models, 24-hour completion window
- Anthropic Message Batches: 50% discount, up to 24-hour completion
For workloads like nightly classification, bulk summarization, or offline embedding generation, the batch discount roughly halves the cost numbers above.
Context Windows and Long-Document Handling
Anthropic's 200K token context window is a concrete technical advantage for specific use cases:
- Full codebase review: A 100K-token codebase fits in a single Claude request; GPT-4o would require chunking
- Legal document analysis: Long contracts, regulatory filings, or research papers often exceed 128K tokens
- Long-running conversations: Claude can maintain full context for much longer conversations without truncation
For applications where context length is not a constraint (typical chatbots, most API tasks, code generation for single files), the 128K vs 200K difference is irrelevant.
Which SDK to Choose
OpenAI is the better default if:
- You need DALL-E 3 image generation or Sora video generation
- You need Whisper speech-to-text
- You need text-to-speech (TTS)
- You're using Assistants API with file search and persistent threads
- GPT-4o mini pricing is critical for high-volume simple tasks
- Your team has more existing OpenAI experience
- You need the broadest third-party integration ecosystem (LangChain, LlamaIndex, etc.)
Anthropic is the better default if:
- You need the longest context window (200K tokens)
- Your workload is text generation, reasoning, and instruction-following where Claude's benchmarks lead
- Streaming code readability matters to your team
- You're building complex agents where typed error subclasses simplify error handling
- You want to evaluate the best current model on long-context tasks without paying Opus prices
For authentication alongside your AI application, the Auth0 vs Clerk comparison covers securing the user-facing layer of AI-powered products.
Framework and Integration Context
Both SDKs integrate with major AI orchestration frameworks:
- LangChain: Official integrations for both OpenAI (
langchain-openai) and Anthropic (langchain-anthropic) - LlamaIndex: Both supported via first-party connectors
- Vercel AI SDK: Both supported via
@ai-sdk/openaiand@ai-sdk/anthropic - Mastra, CrewAI: Both providers supported
For Next.js applications, the Vercel AI SDK provides a unified streaming interface over both SDKs — the streamText and generateText functions accept both OpenAI and Anthropic models through a consistent provider API. This is the recommended approach for Next.js AI applications as it decouples your route handlers from the specific SDK.
For teams considering tRPC for their AI application's internal API layer, the Hono RPC vs tRPC vs ts-rest comparison on PkgPulse covers how type-safe API clients complement AI SDK integration. For broader questions about AI API architecture — whether to use REST, GraphQL, or gRPC — the REST vs GraphQL vs gRPC comparison covers the transport layer decisions that co-occur with AI integration work.
Conclusion
The two SDKs are converging in 2026. Both support TypeScript with full type coverage, both have streaming via async iterables, both support tool use and vision. The implementation differences are real but not dramatic.
The decision should be driven by model capability for your specific task, pricing at your expected volume, and context window requirements — not SDK ergonomics. Both SDKs are well-maintained and production-ready.
For most new projects: start with the model that performs best on your specific task (evaluate both), pick the SDK that wraps it, and use the Vercel AI SDK or LangChain if you want the option to switch providers without rewriting API calls.
Methodology and Sources
Pricing data sourced from OpenAI and Anthropic pricing pages, verified Q1 2026. Context window sizes from official model documentation. Code examples verified against SDK versions current as of Q1 2026. Verify current pricing and model availability at the respective provider pricing pages before production deployment.
Explore this API
View anthropic on APIScout →