Switching to Heimdallm
Heimdallm keeps the OpenAI request and response shapes, so most integrations move by changing two strings: the base URL and the API key. This page maps what transfers untouched, what to review, and the differences worth knowing about — whether you come from OpenAI, OpenRouter, or any client built against the OpenAI dialect.
The two-line change
import OpenAI from 'openai';
const client = new OpenAI({
baseURL: '${API_BASE_URL}',
apiKey: process.env.HEIMDALL_API_KEY,
});
import os
from openai import OpenAI
client = OpenAI(
base_url="${API_BASE_URL}",
api_key=os.environ["HEIMDALL_API_KEY"],
)
Create the key in your account — see Quickstart. Keys start with
sk-hd-v1-.
Then change what you ask for: model ids are author/slug, and the author is the creator of
the weights, not the serving company. Browse the catalogue or call
GET /models for the exact ids; copy them verbatim, including any
variant suffixes.
What transfers untouched
- Chat completions — request body, response object, streaming chunks, tool calling,
response_format(includingjson_schema), message content parts including images. - Text completions, embeddings, transcriptions — the same paths and shapes as OpenAI
(
/completions,/embeddings,/audio/transcriptions, multipart included). - Model retrieval —
client.models.retrieve("author/slug")works as it stands. The SDKs percent-encode the id into one path segment, and that path answers the bare model object they read off the result. See SDK retrieval. - Sampling parameters —
temperature,top_p,max_tokens,stop,seed, penalties and the rest are forwarded to the serving endpoint as sent. - Streaming — server-sent events in the OpenAI format; the final chunk before
data: [DONE]carries the usage totals. See the streaming guide.
Heimdallm adds two fields to chat-completion responses — provider (the endpoint that served you) and
usage.cost (the exact USD charge) — which unaware clients can ignore.
Coding harnesses and other API dialects
Choose the reference for the wire protocol your client sends. Each route needs a Heimdallm bearer API key and a marketplace model id; using an Anthropic or OpenAI dialect does not select a model from that company.
- Claude Code and Messages clients: Messages describes the request,
tool blocks and event stream. Count message tokens describes the
input-size estimate clients can use for compaction. Point the client at this API with
ANTHROPIC_BASE_URLand setANTHROPIC_API_KEYto a Heimdallm API key; the SDKs send that value asx-api-key, which authenticates the same as anAuthorization: Bearerheader. Anthropic's hosted web search is refused rather than run, so search through a tool your client runs (Web search). - Codex and Responses clients: Responses describes input items, function tools and named events. Send the full conversation each turn. Stored-response chaining, background execution and built-in server tools are not available.
- Chat-completion clients: Chat completions describes OpenAI chat messages, routing preferences and usage fields.
The client must target the API base URL above and append the route shown in its reference.
Messages and Responses use their own event formats and do not include chat's provider or
usage.cost body fields. Check the chosen reference's limitations before enabling client features.
Coming from OpenRouter
The compatibility is close to total, by design:
| OpenRouter feature | On Heimdallm |
|---|---|
models fallback array | Accepted — up to 20 fallbacks tried in order after the primary model's endpoints. |
provider preferences | Same shape: order, allow_fallbacks, require_parameters, data_collection, zdr, only, ignore, quantizations, sort, max_price. max_price.image and max_price.audio are accepted and bound nothing; output media ceilings are max_price.image_output (per image), max_price.video_second (per second of video) and max_price.image_megapixel (per megapixel of image output). max_price.audio_second bounds each second of audio input. |
| Routing suffixes | :free, :nitro, :floor work the same way. |
usage: { "include": true } | Accepted for compatibility; usage.cost is returned either way. |
| Key format | sk-hd-v1-; keys in the older sk-or-v1- format keep working. |
Two things to review: model ids are author/slug catalogue ids (the author being the weights
creator), and billing is prepaid — deposits fund a balance and requests draw it down, rather
than invoicing after the fact. See Pricing & billing.
Differences to handle once
The error envelope. Every error is one shape:
{
"error": {
"code": 401,
"message": "Missing or invalid API key",
"metadata": { "error_type": "authentication" }
}
}
Branch on metadata.error_type, a stable machine-readable classifier — not on message, which
is written for humans and may change. The full table is in
Errors & rate limits.
Prepaid credits. A request that cannot be covered by the spendable balance fails with 402
before any provider is contacted. If your integration treats 402 as fatal, decide what it
should do instead — surface it, queue, or alert. The metadata distinguishes an empty balance
from a breached spend limit (metadata.scope names which ceiling).
Credit holds. Before a request runs, Heimdallm holds a conservative estimate — prompt plus
your max_tokens ceiling, both as the chosen endpoint will receive them. A ceiling the window
trims and a history compression shortens are held at their trimmed and shortened sizes, so the
hold is bounded by the endpoint's window however large a ceiling an integration sends "to be
safe". Send a realistic ceiling anyway; see Pricing & billing.
Model availability is real. A marketplace serves what providers serve: a model can have many
endpoints or one, and a 503 with metadata.rejections means the model exists but no live
endpoint satisfied your constraints. Loosen the constraints or add
fallback models.
One n. Chat completions accept n: 1 only — ask for one completion per request, as many
times as you like.
Other small edges. Transcription responses add usage.seconds (billed per second of audio,
not per token). The catalogues and account endpoints (GET /key, GET /credits,
GET /generation, GET /activity) are Heimdallm's own — see
Key & activity.
Frameworks and other clients
Anything that lets you set an OpenAI base URL works: LangChain and LlamaIndex chat and embedding
models, LiteLLM, the community SDKs, and hand-rolled fetch clients. Configure the base URL and
key, and treat the response as an OpenAI response, ignoring fields you do not know.
A migration checklist
- Swap
baseURLand the key; setHEIMDALL_API_KEYin your environment. - Replace model ids with catalogue ids; decide on routing — plain,
suffixed, or explicit
providerpreferences. - Deposit and set spend limits — per key for integration-level caps.
- Handle
402and429(respectRetry-After) deliberately rather than by accident. - Watch real spend: log
X-Generation-Id, reconcile againstusage.cost, and checkGET /activityfor daily totals.