Dokumentation

PrimaAPI

Eine OpenAI-kompatible API für Inferenz, die in Schweizer Rechenzentren läuft. Wenn Ihr Code heute mit einem OpenAI-SDK spricht, sind es zwei Zeilen: eine andere Base-URL und ein anderer Auth-Header.

Early Access OpenAI-kompatibel Inferenz in der Schweiz

Schnellstart

Drei Dinge, dann läuft der erste Aufruf: ein Schlüssel, die Base-URL, der Header. Wer schon ein OpenAI-SDK im Projekt hat, ändert nur die letzten zwei.

  1. Schlüssel ausstellen — Sie sehen ihn genau einmal. Legen Sie ihn als Umgebungsvariable ab, etwa PRIMA_API_KEY.
  2. Base-URL: https://api.prima.li/api/prima/v1
  3. Auth-Header: X-API-Key: <Ihr Schlüssel> — nicht Authorization: Bearer.
bash
curl https://api.prima.li/api/prima/v1/chat/completions \
  -H "X-API-Key: $PRIMA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "prima-core",
    "messages": [
      {"role": "user", "content": "Fasse in einem Satz zusammen: Prima ist eine Schweizer KI-API."}
    ],
    "max_tokens": 200
  }'

Antwort, unverändert:

json · echte Antwort
{
  "id": "primaapi_563afc5b719a4db0b23213a869182034",
  "object": "chat.completion",
  "model": "prima-core",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Prima ist die erste Schweizer KI-API für nahtlose Integration von Sprachmodellen in Anwendungen."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 31,
    "completion_tokens": 23,
    "total_tokens": 54
  },
  "prima": {
    "alias": "prima-core",
    "request_id": "primaapi_563afc5b719a4db0b23213a869182034"
  }
}

Authentifizierung

Ein Header, immer derselbe: X-API-Key. Es gibt keine zweite Variante und keinen Fallback. Ein API-Schlüssel als Bearer-Token allein wird nicht akzeptiert — dieser Pfad prüft Sitzungs-Token, keine Schlüssel.

Kein Workaround nötig

Ihr SDK darf seinen eigenen Authorization: Bearer-Header mitschicken. Er wird ignoriert, solange ein gültiger X-API-Key danebensteht — Sie müssen ihn also nicht entfernen. Gemessen am 3. August 2026 gegen die Live-API, mit gültigem wie mit absichtlich falschem Bearer-Wert.

GesendetErgebnis
X-API-Key: prima_pk_… 200 — der normale Weg.
Authorization: Bearer prima_pk_… allein 401. Ein API-Schlüssel ist kein Bearer-Token.
beide Header zusammen 200. X-API-Key gewinnt; Authorization wird nicht geprüft — auch dann nicht, wenn er Unsinn enthält.
gar nichts 401 Missing credentials.

Schlüssel tragen genau einen Scope: ai. Damit erreichen sie den Gateway unter /api/prima/v1/ und sonst nichts — kein anderer Teil der Plattform ist damit ansprechbar. Ein Prima-Schlüssel kann insbesondere keine weiteren Schlüssel ausstellen; das geht nur angemeldet, hier im Portal.


OpenAI-Kompatibilität

Die Endpunkte /chat/completions, /embeddings und /models haben die Form, die OpenAI-Clients erwarten. Sie behalten Ihr SDK, Ihre Typen und Ihre Retry-Logik. Zwei Änderungen genügen:

  1. base_url auf https://api.prima.li/api/prima/v1.
  2. X-API-Key als Default-Header setzen. Den Authorization-Header des SDKs können Sie stehen lassen.

Mehr ist es nicht. Beide Schnellstarts oben sind genau in dieser Form gegen die Live-API ausgeführt worden.

Was gleich ist

  • Request- und Response-Form inklusive usage.
  • messages, temperature, max_tokens.
  • tools und tool_choice werden durchgereicht.
  • Das Feld model in der Antwort enthält immer den Alias, den Sie geschickt haben — nie einen internen Modellnamen.

Was anders ist

  • Streaming funktioniert. stream: true liefert einen gewöhnlichen SSE-Strom aus chat.completion.chunk-Objekten, abgeschlossen mit data: [DONE]. Details unter Streaming.
  • Ein Zusatzfeld prima in jeder Antwort, mit alias und request_id. OpenAI-Clients ignorieren unbekannte Felder, es bricht also nichts.
  • Andere Modellnamen. Vier Aliase, nach Aufgabe benannt: prima-quick, prima-core, prima-deep, prima-embed.

Chat Completions

POST https://api.prima.li/api/prima/v1/chat/completions

FeldTypBedeutung
modelstring, Pflicht Ein Alias. Ein interner Modellname wird nicht akzeptiert.
messagesarray, Pflicht Wie bei OpenAI: role und content.
max_tokensinteger Obergrenze für die sichtbare Ausgabe. Keine Obergrenze für die abgerechneten Tokens — siehe Tokens & Abrechnung.
temperaturefloat, Standard 0.7—
tools, tool_choicearray / string Werden an das Modell weitergereicht; Tool-Calls kommen im gewohnten Format zurück.
streambool Beides funktioniert. true liefert SSE-Chunks, der usage-Block kommt im letzten Chunk vor [DONE].

Der prima-Block

Jede Antwort trägt ihn. request_id ist die Nummer, die Sie uns bei einer Rückfrage nennen — sie steht auch im Zähler, ohne dass dort je ein Inhalt gespeichert würde.

Bei prima-deep kann er zusätzlich max_tokens_floor_applied enthalten. Das Modell denkt vor dem Antworten, und mit einem knappen Limit bricht es mitten im Denken ab und liefert null Zeichen. Statt das zu tun, hebt der Gateway das Limit auf 8000 an und schreibt es in die Antwort:

json · echte Antwort (gekürzt)
"prima": {
  "alias": "prima-deep",
  "request_id": "primaapi_5202820bd9214f56a10a344b6064bf3b",
  "max_tokens_floor_applied": true,
  "requested_max_tokens": 1000,
  "effective_max_tokens": 8000
}

Was das für die Rechnung bedeutet, steht unter Tokens & Abrechnung — mit gemessenen Zahlen.


Embeddings

POST https://api.prima.li/api/prima/v1/embeddings — input ist ein String oder ein Array von Strings.

bash
curl https://api.prima.li/api/prima/v1/embeddings \
  -H "X-API-Key: $PRIMA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "prima-embed",
    "input": ["Vertragsklausel A", "Vertragsklausel B"]
  }'
json · echte Antwort (Vektoren gekürzt)
{
  "object": "list",
  "model": "prima-embed",
  "data": [
    { "object": "embedding", "index": 0, "embedding": [1.359375, -1.84375, … 3584 Werte] },
    { "object": "embedding", "index": 1, "embedding": [0.76953125, -2.15625, … 3584 Werte] }
  ],
  "prima": {
    "alias": "prima-embed",
    "request_id": "primaapi_38d2ac0d2c1840dab8ba20d453d63b84",
    "request_count": 2
  }
}

Die Vektoren haben 3584 Dimensionen. Embeddings werden pro Anfrage gezählt, nicht pro Token — ein Array mit zwei Strings sind zwei Anfragen, wie request_count in der Antwort zeigt. Der Zähler spiegelt damit exakt, wie unser Vorlieferant abrechnet; eine eigene Kunstwährung gibt es nicht.

Modelle auflisten

bash
curl https://api.prima.li/api/prima/v1/models -H "X-API-Key: $PRIMA_API_KEY"

Acht Einträge: vier gleitende Aliase und zu jedem ein festgeschriebenes Gegenstück. Jeder Eintrag beschreibt sich selbst über prima_kind, prima_floating und prima_pinned_variant — bauen Sie Ihre Auswahl auf diesen Feldern auf, nicht auf einer Liste von Namen in Ihrem Code. Zur Modellreferenz.


Tokens & Abrechnung

Bitte einmal lesen, bevor Sie hochskalieren

Bei prima-deep begrenzt max_tokens Ihre Kosten nicht — der Gateway hebt den Wert selbst an. Bei prima-quick und prima-core tut er das nicht. Der Unterschied ist gross genug, um ihn nicht zu raten.

prima-deep denkt, bevor es antwortet. Diese Denkschritte sind Tokens, sie werden abgerechnet, und sie stehen nicht in der Antwort. Damit das Modell nicht mitten im Denken abgeschnitten wird und Sie für eine unbrauchbare Antwort zahlen, hebt der Gateway max_tokens auf einen Mindestwert an — und sagt Ihnen das im prima-Block.

Sechs Aufrufe vom 3. August 2026, alle mit derselben Frage „Antworte nur mit: ja“:

Aliasmax_tokenssichtbare Antwort abgerechnete completion_tokenseffective_max_tokens
prima-quick16"ja"2—
prima-quick256"ja"2—
prima-core16"ja"2—
prima-core256"ja"2—
prima-deep16" ja"3668000
prima-deep256" ja"3088000

Zwei Tokens gegen dreihundert, für dieselbe Antwort. Wenn Sie prima-deep einsetzen, dann für Aufgaben, bei denen das Nachdenken den Preis wert ist — nicht als Standardmodell. Wofür welcher Alias gedacht ist, steht in der Modellreferenz.

Ob der Gateway eingegriffen hat, steht in jeder Antwort — im prima-Block als max_tokens_floor_applied. Prüfen Sie im Zweifel den, statt aus max_tokens auf die Kosten zu schliessen; das Feld ist oben unter OpenAI-Kompatibilität abgedruckt.

Was daraus folgt, ganz nüchtern:

  • max_tokens ist keine Kostenbremse. Es begrenzt die sichtbare Ausgabe, nicht das Denken davor. Im Beispiel oben werden bei einem Limit von 16 gemessene 115 Tokens abgerechnet.
  • Rechnen Sie mit dem, was usage zurückgibt, nicht mit einer Schätzung aus Ihrem Prompt. Der Wert steht in jeder Antwort.
  • Kurze Antworten sind nicht billig. Ein Klassifikator, der ein Wort zurückgibt, kostet dreistellig Tokens pro Aufruf. Bei zehntausend Aufrufen am Tag ist das die Zahl, die zählt.
  • Die harte Grenze ist Ihr Guthaben, nicht ein Tarif. Ist es aufgebraucht, lehnt der Gateway neue Arbeit ab, statt weiterzurechnen und hinterher zu belasten. Sie können zusätzlich ein eigenes Monatslimit setzen — Ausgabenlimit.

Was gezählt wird — und was nicht

Der Zähler kennt Zeitpunkt, Alias, internes Modell, Token- und Anfragezahlen sowie die request_id. Inhalte kennt er nicht. Es gibt in der Zählertabelle keine Spalte, in der ein Prompt oder eine Antwort landen könnte — auch nicht versehentlich. Deshalb kann Ihr Verbrauch Ihnen zeigen, wie viel Sie verbraucht haben, und nicht, was Sie geschrieben haben. Das ist keine fehlende Funktion, das ist die Zusage.


Fehler & Grenzen

Form einer Fehlerantwort

Fehler sind OpenAI-förmig: ein Objekt unter error, nicht ein String und nicht detail. Wer die Meldung auslesen will, liest error.message; wer verzweigen will, verzweigt auf error.code.

json · echte Antwort
{
  "error": {
    "message": "Missing credentials",
    "type": "authentication_error",
    "param": null,
    "code": "AUTH_001",
    "request_id": "6e80a491-8385-46c6-85d5-48663b76af4c"
  }
}

request_id ist in jeder Antwort enthalten, auch im Fehlerfall. Nennen Sie sie, wenn Sie uns schreiben.

Leeres input

json · echte Antwort
{
  "error": {
    "message": "`input` must be non-empty",
    "type": "invalid_request_error",
    "param": null,
    "code": "SRV_001",
    "request_id": "723bf49f-dd28-479c-bec0-bcdace8ba229"
  }
}

Statuscodes

CodeBedeutung
200Antwort da. Mit stream: true als SSE-Strom, sonst als ein Objekt.
400Unbekannter Alias oder leeres input. Die Meldung nennt den Grund und listet bei einem Alias-Fehler die gültigen Werte auf.
401Kein oder falsches Credential. Ein zusätzlich mitgeschickter Authorization-Header ist kein Grund — der wird ignoriert.
402Guthaben aufgebraucht (code: insufficient_credit). Aufladen auf prima.li. Greift nur, wenn die Guthabenprüfung aktiv ist — heute ist sie es nicht.
403Der Schlüssel existiert, trägt aber nicht den ai-Scope, oder er wird ausserhalb von /api/prima/v1/ verwendet.
429Entweder gewöhnliche Drosselung, oder ein Verbrauchslimit ist erreicht (code: spend_cap_exceeded). Im zweiten Fall hilft Warten nicht. Geprüft wird vor dem Modellaufruf — eine abgelehnte Anfrage kostet nichts.
502Der Modellanbieter hat nicht geliefert. Wiederholen.
503Der Gateway ist nicht konfiguriert, oder die Guthabenverwaltung antwortet nicht (code: credits_unavailable). Beides unser Fehler, nicht Ihrer.

Unbekannter Alias

Der Fehler listet auf, was gültig gewesen wäre:

json · echte Antwort
{
  "error": {
    "message": "Unknown PrimaAPI model alias 'prima-turbo'. Valid: ['prima-core', 'prima-core-2026-08', 'prima-deep', 'prima-deep-2026-08', 'prima-quick', 'prima-quick-2026-08']",
    "type": "invalid_request_error",
    "param": null,
    "code": "SRV_001",
    "request_id": "c8a7303a-c65a-498a-9353-28c6e513634c"
  }
}

Streaming

stream: true wird unterstützt. Der Gateway antwortet mit einem gewöhnlichen SSE-Strom aus chat.completion.chunk-Objekten und schliesst mit data: [DONE] ab. Das offizielle OpenAI-SDK iteriert ihn ohne Sonderbehandlung — dieselben zwei Zeilen Konfiguration wie oben genügen.

typescript
const stream = await client.chat.completions.create({
  model: "prima-core",
  messages: [{ role: "user", content: "Zaehle von 1 bis 5." }],
  max_tokens: 100,
  stream: true,
});

for await (const chunk of stream) {
  const delta = chunk.choices[0]?.delta?.content;
  if (delta) process.stdout.write(delta);
}
ausgabe
ausgeführt gegen die Live-API, openai 7.3.0 auf Node v22.22.2

Hier ist die Zählung von 1 bis 5:

1. eins
2. zwei
3. drei
4. vier
5. fünf

Wo der usage-Block bleibt: im letzten Chunk vor [DONE], zusammen mit dem prima-Block. Die Chunks davor tragen nur delta. Wer die Tokenzahl braucht, muss den Strom also zu Ende lesen und darf nicht beim ersten finish_reason aussteigen.

Korrektur

Frühere Fassungen dieser Seite behaupteten, stream: true werde mit 400 abgelehnt. Das galt einmal und gilt nicht mehr; nachgemessen am 3. August 2026 gegen die Live-API. Falls Sie Code haben, der deshalb stream: false erzwingt: die Einschränkung ist weg.

Weitere Grenzen, offen benannt

  • Early Access. PrimaAPI läuft, ist aber nicht allgemein verfügbar. Zugang bekommt man auf Anfrage.
  • Kein Requests-pro-Sekunde-Limit. Verbrauchsgrenzen sind derzeit die einzige Verkehrssteuerung. Bauen Sie in Ihrem Client trotzdem ein Backoff ein.
  • Keine ISO-27001-Zertifizierung. Wir arbeiten darauf hin, halten sie aber nicht. Was wir zusagen, ist der Ort der Verarbeitung und ein unterschreibbarer Auftragsverarbeitungsvertrag — nicht ein Zertifikat, das wir nicht haben.

Wenn etwas fehlt

Schreiben Sie an prima@alchemy.zuerich und nennen Sie die request_id. Wir sehen damit den Aufruf im Zähler — Zeitpunkt, Alias, Grösse. Ihren Prompt sehen wir nicht, und das können wir auch dann nicht ändern, wenn Sie uns darum bitten.

Alle Beispiele und Zahlen auf dieser Seite gegen die Live-API geprüft am 3. August 2026.