
Use Claude with the OpenAI SDK: OpenAI-compatible endpoint, streaming, tools and limits
Code written for the OpenAI SDK can call Claude without a rewrite: point base_url at the gateway's /v1, use a gateway key and a Claude model ID. The endpoint accepts Chat Completions requests, streams in the same format and supports function calling. A few Claude features only exist in Anthropic's native format; the last section covers when to switch.
Endpoint facts
| Item | Value |
|---|---|
| Base URL | https://aiprimetech.io/v1 |
| Chat endpoint | POST https://aiprimetech.io/v1/chat/completions |
| Model list | GET https://aiprimetech.io/v1/models |
| Auth | Authorization: Bearer <key>; x-api-key is also accepted |
| Models | Claude IDs such as claude-sonnet-5; GPT IDs with a key from the Codex group |
Python
pip install openaiimport os
from openai import OpenAI
client = OpenAI(base_url="https://aiprimetech.io/v1", api_key=os.environ["AIPRIME_API_KEY"])
resp = client.chat.completions.create(
model="claude-sonnet-5",
max_tokens=1024,
messages=[
{"role": "system", "content": "You are a concise assistant."},
{"role": "user", "content": "Explain idempotency keys in two sentences."},
],
)
print(resp.choices[0].message.content)
print(resp.usage)base_url must end in /v1: the SDK appends /chat/completions to it. Without /v1 the request lands on the website and returns HTML; with /v1 twice it returns 404 page not found.
Node.js
import OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://aiprimetech.io/v1", apiKey: process.env.AIPRIME_API_KEY });
const resp = await client.chat.completions.create({
model: "claude-sonnet-5",
max_tokens: 1024,
messages: [{ role: "user", content: "Write a haiku about retries." }],
});
console.log(resp.choices[0].message.content);Streaming
stream = client.chat.completions.create(
model="claude-sonnet-5",
max_tokens=2048,
stream=True,
stream_options={"include_usage": True},
messages=[{"role": "user", "content": "Outline a zero-downtime migration."}],
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
if chunk.usage:
print("\n", chunk.usage)Stream long answers: a non-streaming request has to finish inside your client's timeout, a stream does not. include_usage adds a final chunk with token counts. More on why it matters in streaming LLM responses.
Function calling
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Current weather for a city",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
},
}]
resp = client.chat.completions.create(
model="claude-sonnet-5", max_tokens=512, tools=tools,
messages=[{"role": "user", "content": "What is the weather in Paris?"}],
)
call = resp.choices[0].message.tool_calls[0]
print(call.function.name, call.function.arguments) # get_weather {"city": "Paris"}The reply comes back with finish_reason: "tool_calls" and OpenAI-style tool_calls. Run the function, append a tool message with the result and the matching tool_call_id, then call again, exactly as with OpenAI models.
Reasoning and thinking
reasoning_effort maps to Claude's effort setting and accepts low, medium, high, xhigh or max. The value none is rejected. When the model thinks, its reasoning arrives in a separate reasoning_content field next to content, so code that reads message.content keeps working unchanged.
resp = client.chat.completions.create(
model="claude-opus-5-5", max_tokens=4096, reasoning_effort="high",
messages=[{"role": "user", "content": "Find the bug in this function: ..."}],
)Models
| Model ID | Use it for |
|---|---|
claude-sonnet-5 | Default for coding and agent work: fast, strong, mid-priced. |
claude-opus-5-5 | Harder reasoning, large refactors, long agent runs. |
claude-fable-5-1 | Most capable and most expensive; save it for the hardest tasks. |
claude-haiku-4-5 | Cheap and quick: titles, summaries, classification, small steps. |
Use these IDs exactly. Old dated IDs such as claude-3-5-sonnet-20241022 still appear in some model lists but are not served and return a model-not-supported error.
When to use the native Anthropic API instead
The OpenAI format covers chat, streaming, tools and images. Use Anthropic's own Messages format, at the same gateway, when you need explicit prompt-cache breakpoints, thinking blocks you pass back between turns, PDF documents or citations. The Anthropic Python SDK guide and the Vercel AI SDK guide cover that route.
Troubleshooting
| What you see | Cause | Fix |
|---|---|---|
A JSON parse error, response starting with < | base_url without /v1 | https://aiprimetech.io/v1 |
404 page not found | /v1 twice | Exactly one /v1 |
401 Invalid API key | Missing or wrong key in the process | Check the variable; a key belongs to one gateway |
| Model not supported | Old dated or misspelled model ID | Use an ID from the table above |
'none' is not supported | reasoning_effort="none" | Use low or higher, or leave it out |
429 Concurrency limit exceeded for user | Too many requests in flight | Limit parallelism; see rate limits and retries |
| Timeouts on long answers | Long non-streaming request | Stream, or raise the client timeout |
Frequently asked questions
Can I use Claude with the OpenAI SDK?
What base URL do I use for Claude in the OpenAI SDK?
Does function calling work with Claude through the OpenAI format?
When should I use the native Anthropic API instead?
Get an API key — no Anthropic account or waitlist required.
Get your API keyAI Prime Tech is an independent API gateway. It is not affiliated with, endorsed by, or a reseller of Anthropic. Claude and related model names are trademarks of their respective owners.