PL Log inCreate account
Launching in October 2026. Accounts, model catalog, calculator and docs are live. Studio is available in preview; generation is awaiting activation.

Call Claude, Gemini, DeepSeek and GPT with the OpenAI SDK

Use the OpenAI Python and Node SDKs to call Claude, Gemini, DeepSeek, GLM and GPT by changing base_url. Streaming, fallbacks, cost routing and tools.

You can call Claude, Gemini, DeepSeek, GLM and GPT models with the official OpenAI SDK by pointing it at an OpenAI-compatible endpoint: set base_url="https://jantaris.com/v1", use your sk-jt-... key, and pass a model id like claude-sonnet-5 or gemini-3.8-flash. The request and response format stays the standard Chat Completions shape, including streaming, so existing code keeps working.

I moved several of my own projects to this setup so I could swap models without rewriting clients. Here is the code for Python and Node, plus the patterns that made it worth it: per-task model routing and automatic fallback.

Why one SDK for every chat model

Every major vendor ships its own SDK. They are all good, and all different: message formats, streaming events, error classes, retry logic. Supporting three vendors means three code paths.

The OpenAI Chat Completions format became the lingua franca. Most tools (Cursor, n8n, Make, LangChain, LlamaIndex, countless internal scripts) can talk to it. If one endpoint serves every model in that format, the model becomes a string you change in config.

With Jantaris that endpoint is https://jantaris.com/v1/chat/completions, and the same key and prepaid balance also cover image, video and music models. More on that in One API for image, video, music and chat models.

Models you can call through the OpenAI SDK

Model idVendorWhat I use it forPrice
claude-opus-5.5AnthropicHard reasoning, long documents, complex code$5.40 / $27.00 per 1M tokens
claude-sonnet-5AnthropicEveryday coding and writing$3.24 / $16.20 per 1M tokens
gpt-6-solOpenAICoding and agent workflows, mid-tier cost$2.16 / $10.80 per 1M tokens
gpt-6-lunaOpenAIHigh-volume extraction, classification, summaries$0.108 / $0.54 per 1M tokens
gemini-3.8-flashGoogleFast general work, coding, multi-step tasks
deepseek-v4.1-flashDeepSeekCheap long-context work$0.54 / $1.75 per 1M tokens
glm-5.3Z.aiAlternative for coding and general chat

Model pages with full details: Claude Opus 5.5, Claude Sonnet 5, GPT-6 Sol, GPT-6 Luna, gemini-3.8-flash, DeepSeek V4.1 Flash, glm-5.3. The live catalog is always at GET /v1/models.

Python: the OpenAI SDK with a custom base_url

bash
pip install openai
export AI_API_KEY=sk-jt-...
python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://jantaris.com/v1",
    api_key=os.environ["AI_API_KEY"],
)

for model in ["claude-sonnet-5", "gemini-3.8-flash", "deepseek-v4.1-flash", "gpt-6-luna"]:
    resp = client.chat.completions.create(
        model=model,
        messages=[
            {"role": "system", "content": "Answer in one sentence."},
            {"role": "user", "content": "What is idempotency in HTTP APIs?"},
        ],
        max_tokens=120,
    )
    print(f"{model}: {resp.choices[0].message.content}")

Same client, four vendors. The only thing that changes is model.

Streaming responses

stream=True works as it does with OpenAI:

python
stream = client.chat.completions.create(
    model="claude-opus-5.5",
    messages=[{"role": "user", "content": "Explain CAP theorem with a real example."}],
    stream=True,
)
for chunk in stream:
    delta = chunk.choices[0].delta.content if chunk.choices else None
    if delta:
        print(delta, end="", flush=True)

Node.js and TypeScript

bash
npm install openai
javascript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://jantaris.com/v1",
  apiKey: process.env.AI_API_KEY,
});

const resp = await client.chat.completions.create({
  model: "gpt-6-sol",
  messages: [{ role: "user", content: "Write a regex that matches ISO 8601 dates." }],
});
console.log(resp.choices[0].message.content);

const stream = await client.chat.completions.create({
  model: "gemini-3.8-flash",
  messages: [{ role: "user", content: "List five uses for a Raspberry Pi." }],
  stream: true,
});
for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}

Note the Node SDK uses baseURL, the Python SDK uses base_url.

Plain HTTP with curl

No SDK needed. This is handy for debugging and for tools that only let you set a URL and headers:

bash
curl https://jantaris.com/v1/chat/completions \
  -H "Authorization: Bearer sk-jt-..." \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-v4.1-flash",
    "messages": [{"role": "user", "content": "Summarize the plot of Hamlet in 3 bullet points."}]
  }'

Route each task to the right model

This is where one endpoint pays off. I keep a small routing table and pick the model by task type, not by which SDK happens to be installed:

python
ROUTES = {
    "extract": "gpt-6-luna",          # structured fields from text, high volume
    "classify": "deepseek-v4.1-flash",
    "draft": "claude-sonnet-5",
    "code": "gpt-6-sol",
    "review": "claude-opus-5.5",      # when mistakes are expensive
}

def ask(task_type, prompt, **kw):
    return client.chat.completions.create(
        model=ROUTES[task_type],
        messages=[{"role": "user", "content": prompt}],
        **kw,
    ).choices[0].message.content

The table above is my current setup, not a law. Run your own evaluations: take 20 to 50 real examples, send them through two or three candidate models, and compare quality against cost per 1M tokens. Changing the route is a one-line edit.

Automatic fallback between models

Providers have bad days. A fallback chain keeps your app answering when one model returns a server error or you hit a rate limit:

python
import time
from openai import APIStatusError, APIConnectionError

FALLBACK = ["claude-sonnet-5", "gpt-6-sol", "gemini-3.8-flash"]

def chat_with_fallback(messages, **kw):
    last_err = None
    for model in FALLBACK:
        for attempt in range(2):
            try:
                resp = client.chat.completions.create(model=model, messages=messages, **kw)
                return model, resp.choices[0].message.content
            except APIStatusError as e:
                last_err = e
                if e.status_code in (401, 402, 422):
                    raise  # config or balance problem, switching models will not help
                time.sleep(1.5 * (attempt + 1))  # 429 and 5xx: retry, then next model
            except APIConnectionError as e:
                last_err = e
                time.sleep(1.5 * (attempt + 1))
    raise last_err

model, answer = chat_with_fallback([{"role": "user", "content": "Give me a haiku about queues."}])
print(model, answer)

Jantaris already fails over between infrastructure providers for part of the catalog, so the same model can keep answering when one source has trouble. The chain above adds a second layer: switching to a different model altogether.

What each status code means in this context:

CodeMeaningRetry?
401Invalid keyNo
402Balance too lowNo, top up first
404Unknown model idNo, fix the id
422Invalid parametersNo, fix the request
429Rate limitedYes, with backoff
502Upstream provider errorYes, or fall back

Using the Anthropic SDK instead

If your code already uses the Anthropic SDK, you do not need to switch. Jantaris also serves the Anthropic Messages format at /v1/messages:

python
import os
from anthropic import Anthropic

client = Anthropic(base_url="https://jantaris.com", api_key=os.environ["AI_API_KEY"])

snippet = """
def get_user(cur, user_id):
    cur.execute("SELECT * FROM users WHERE id = '%s'" % user_id)
"""

msg = client.messages.create(
    model="claude-opus-5.5",
    max_tokens=512,
    messages=[{"role": "user", "content": "Review this code for SQL injection risks:" + snippet}],
)
print(msg.content[0].text)

Note the base URL without /v1: the Anthropic SDK appends the path itself. This is also how Claude Code connects, covered in Use Claude Code with a custom base URL and prepaid balance.

Frameworks and no-code tools

Anything built on the OpenAI client accepts a base URL. Two common ones:

LangChain

python
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="claude-sonnet-5",
    base_url="https://jantaris.com/v1",
    api_key=os.environ["AI_API_KEY"],
)
print(llm.invoke("Name three sorting algorithms.").content)

n8n, Make, Zapier, Cursor

Use the OpenAI credential or node, set the base URL to https://jantaris.com/v1, paste the sk-jt-... key, and type the model id by hand if it is not in the dropdown. The no-code docs walk through n8n, Make and Zapier step by step.

Pricing and billing

Chat models are billed per 1M input and output tokens at the rates in the table above and on /pricing. All usage comes from one prepaid balance that does not expire, with no subscription. Top-ups start at $10, with ready-made packs of $10, $25, $50, $100 and $250. Prompts and outputs are not used to train models.

Check your balance from code:

python
import requests
print(requests.get("https://jantaris.com/v1/balance",
                   headers={"Authorization": f"Bearer {os.environ['AI_API_KEY']}"}).json())

The endpoint reference is in /docs/chat.

FAQ

Can I use the OpenAI SDK to call Claude?

Yes. Point the OpenAI SDK at an OpenAI-compatible endpoint that serves Claude, such as https://jantaris.com/v1, and set model="claude-sonnet-5" or model="claude-opus-5.5". The request and response use the standard Chat Completions format.

Does streaming work with non-OpenAI models?

Yes. Pass stream=True (Python) or stream: true (Node) and iterate over the chunks exactly as with OpenAI models.

How do I switch between Gemini, DeepSeek and GPT?

Change the model string, for example gemini-3.8-flash, deepseek-v4.1-flash or gpt-6-sol. The client, key and code stay the same.

Do I need separate accounts with each AI vendor?

No. One Jantaris key and one prepaid balance cover every model in the catalog, including chat, image, video and music models.

Does it work with LangChain and n8n?

Yes. Both accept a custom OpenAI base URL. Set it to https://jantaris.com/v1 and use your Jantaris key.