SDKs & frameworks
Every client on this page is configured with two values: a base URL and a Heimdallm API key
(sk-hd-v1-…, see Authentication). Model ids are author/slug catalogue ids,
copied verbatim from the catalogue including any variant suffix.
Two base URLs cover everything here. The OpenAI wire and the OpenRouter conventions are served under
https://api-staging.impossiblecarrot.cc/api/v1
where a client appends /chat/completions, /embeddings or /models. The Anthropic Messages dialect is
served under
https://api-staging.impossiblecarrot.cc/api
because the Anthropic SDKs append /v1/messages themselves, which lands on the
Messages route.
The Heimdallm SDKs (@heimdallm/sdk, heimdallm-sdk on PyPI) are the first-party clients:
they cover the whole API, including video and mesh jobs, rerank, files, batches, the catalogue and
generation costs, which the clients on this page have no surface for.
What the compatibility matrix runs
A compatibility matrix installs the published SDKs listed below at the versions npm resolves, points each at a running marketplace with a real key, and records pass or fail per capability: chat, streaming, tools, structured outputs, embeddings, usage and cost, and the error envelope. A client the matrix does not run follows its own documented OpenAI-compatible settings; nothing here claims a pass for it.
| Client | Package | Matrix row |
|---|---|---|
| OpenAI Node SDK | openai | openai-node |
| Anthropic Node SDK | @anthropic-ai/sdk | anthropic-node |
| Vercel AI SDK via the OpenRouter provider | @openrouter/ai-sdk-provider | vercel-ai-openrouter |
| OpenAI Python SDK | openai (PyPI) | none |
| LiteLLM | litellm | none |
| LangChain | langchain-openai | none |
| LlamaIndex | llama-index-llms-openai-like | none |
OpenAI SDKs
Matrix row openai-node. Change baseURL and the key; everything else in an existing integration
stays.
import OpenAI from 'openai';
const client = new OpenAI({
baseURL: 'https://api-staging.impossiblecarrot.cc/api/v1',
apiKey: process.env.HEIMDALL_API_KEY,
});
const completion = await client.chat.completions.create({
model: 'deepseek/deepseek-v4-flash-0731-abliterated',
messages: [{ role: 'user', content: 'Name the capital of Iceland.' }],
});
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api-staging.impossiblecarrot.cc/api/v1",
api_key=os.environ["HEIMDALL_API_KEY"],
)
completion = client.chat.completions.create(
model="deepseek/deepseek-v4-flash-0731-abliterated",
messages=[{"role": "user", "content": "Name the capital of Iceland."}],
)
The same client reaches embeddings, speech, transcription and rerank at the paths in the
API reference. The reply carries two members the OpenAI types do not declare, provider and
usage.cost; both are on the parsed object as extra members and in the raw body. Streaming ends with
a final chunk whose choices array is empty and whose usage is complete — see the
streaming guide.
Anthropic SDKs
Matrix row anthropic-node. The SDK appends /v1/messages to the base URL it is given, so the base
URL is the API origin plus /api. It sends the key as x-api-key, which the gateway accepts like an
Authorization: Bearer header.
import Anthropic from '@anthropic-ai/sdk';
const client = new Anthropic({
baseURL: 'https://api-staging.impossiblecarrot.cc/api',
apiKey: process.env.HEIMDALL_API_KEY,
});
const message = await client.messages.create({
model: 'deepseek/deepseek-v4-flash-0731-abliterated',
max_tokens: 256,
messages: [{ role: 'user', content: 'Name the capital of Iceland.' }],
});
import os
import anthropic
client = anthropic.Anthropic(
base_url="https://api-staging.impossiblecarrot.cc/api",
api_key=os.environ["HEIMDALL_API_KEY"],
)
message = client.messages.create(
model="deepseek/deepseek-v4-flash-0731-abliterated",
max_tokens=256,
messages=[{"role": "user", "content": "Name the capital of Iceland."}],
)
The model is a marketplace id, not an Anthropic model name. The Messages usage object has no cost
member, so read the charge from GET /generation or a chat
completions reply. See Messages for the supported content blocks.
Vercel AI SDK
Matrix row vercel-ai-openrouter. An application that already uses @openrouter/ai-sdk-provider
changes baseURL and the key. Attribution headers it sets (HTTP-Referer, X-OpenRouter-Title or
X-Title) are recorded with the generation, as the chat reference describes.
import { createOpenRouter } from '@openrouter/ai-sdk-provider';
import { embed, generateText } from 'ai';
const heimdall = createOpenRouter({
baseURL: 'https://api-staging.impossiblecarrot.cc/api/v1',
apiKey: process.env.HEIMDALL_API_KEY,
extraBody: { usage: { include: true } },
});
const { text, providerMetadata } = await generateText({
model: heimdall.chat('deepseek/deepseek-v4-flash-0731-abliterated'),
prompt: 'Name the capital of Iceland.',
});
const { embedding } = await embed({
model: heimdall.textEmbeddingModel('baai/bge-m3'),
value: 'Reykjavik is the capital of Iceland.',
});
The charge is at providerMetadata.openrouter.usage.cost. The provider package exposes chat and
embeddings; use the OpenAI SDK for speech, transcription and rerank.
LiteLLM
LiteLLM's openrouter provider sends the OpenRouter wire to whatever api_base it is given. The
model string is openrouter/ followed by the catalogue id.
import os
import litellm
response = litellm.completion(
model="openrouter/deepseek/deepseek-v4-flash-0731-abliterated",
api_base="https://api-staging.impossiblecarrot.cc/api/v1",
api_key=os.environ["HEIMDALL_API_KEY"],
messages=[{"role": "user", "content": "Name the capital of Iceland."}],
)
LiteLLM also reads the base URL from OPENROUTER_API_BASE and the key from OPENROUTER_API_KEY, which
lets one process switch between a deployment and another without a code change. To run LiteLLM as a
proxy in front of Heimdallm, see Gateways.
LangChain
langchain-openai takes the OpenAI-compatible settings directly.
import os
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
chat = ChatOpenAI(
model="deepseek/deepseek-v4-flash-0731-abliterated",
base_url="https://api-staging.impossiblecarrot.cc/api/v1",
api_key=os.environ["HEIMDALL_API_KEY"],
stream_usage=True,
)
embeddings = OpenAIEmbeddings(
model="baai/bge-m3",
base_url="https://api-staging.impossiblecarrot.cc/api/v1",
api_key=os.environ["HEIMDALL_API_KEY"],
check_embedding_ctx_length=False,
)
check_embedding_ctx_length=False makes the client send text instead of token ids, which is what an
embeddings endpoint that is not OpenAI's expects. ChatOpenAI does not extract response members
outside the OpenAI specification, so the reasoning text a reasoning model streams in
delta.reasoning and the provider member are not on its messages; call the OpenAI SDK directly where
they matter.
LlamaIndex
LlamaIndex reaches an OpenAI-compatible endpoint through its OpenAILike classes. Set is_chat_model
so chat messages use /chat/completions, and is_function_calling_model only for a model whose
catalogue entry lists tools in supported_parameters.
import os
from llama_index.llms.openai_like import OpenAILike
from llama_index.embeddings.openai_like import OpenAILikeEmbedding
llm = OpenAILike(
model="deepseek/deepseek-v4-flash-0731-abliterated",
api_base="https://api-staging.impossiblecarrot.cc/api/v1",
api_key=os.environ["HEIMDALL_API_KEY"],
is_chat_model=True,
context_window=32768,
)
embedding = OpenAILikeEmbedding(
model_name="baai/bge-m3",
api_base="https://api-staging.impossiblecarrot.cc/api/v1",
api_key=os.environ["HEIMDALL_API_KEY"],
)
context_window is the model's own context_length from the catalogue; LlamaIndex uses it to size
prompts, and a value larger than the model's is a request the router trims or refuses.
When a client fails
A failure is attributable by its status and body. Every error is the one envelope in
Errors & rate limits, so a client that surfaces the response body shows
error.metadata.error_type. A 401 is the key, a 402 the balance, a 400 the request, and a
503 with metadata.rejections a constraint no live endpoint satisfied; see
Troubleshooting & FAQ. Chat completions accept n: 1 only.