API reference

The OpenAI-compatible data plane.

Interactive OpenAPI reference

The operations below are generated from the canonical OpenAPI specification. Use Test Request to build and send a real request, inspect every field, and view the documented response and error schemas.

Testing an authenticated endpoint sends a real request and may consume credits.

Create a chat completion

post/v1/chat/completions

Create a response

post/v1/responses

Create embeddings

post/v1/embeddings

Transcribe audio

post/v1/audio/transcriptions

Create speech

post/v1/audio/speech

List models

get/v1/models

Check service health

get/healthz

Conventions

  • Model ids are provider/model, e.g. mistral/mistral-medium-3.5.
  • Money is integer micro-EUR (1 EUR = 1,000,000 µEUR), quoted per million tokens.
  • Request bodies are capped at 2 MiB.

Guides and examples

Chat completion

typescript
const res = await client.chat.completions.create({
  model: "mistral/mistral-medium-3.5",
  messages: [{ role: "user", content: "Hallo!" }],
});

Streaming works the same way with "stream": true. See Streaming.

Tool calling works on every chat model whose supported_parameters include tools, streaming and non-streaming. A model without it returns 400 tools_not_supported. Send tools (function tools) with an optional tool_choice and parallel_tool_calls, and the model answers with message.tool_calls (or delta.tool_calls while streaming) and finish_reason: "tool_calls". Continue the loop by sending the assistant turn back with its tool_calls, followed by one {"role": "tool", "tool_call_id": "...", "content": "..."} message per call; on a reasoning model, replay that turn's reasoning_details as well.

A tool definition travels whole, strict and the JSON Schema in parameters included. Nothing tool-related is silently dropped on a path that rebuilds the request: every model on Bedrock, Claude on Vertex, and /v1/responses. There the request is refused with a 400 naming the field when an upstream cannot express a value: tool_choice: "none" and parallel_tool_calls: false on every model served by Bedrock, forcing a tool call ("required" or a named function) together with reasoning on either Claude path, and hosted tools such as web search or MCP, because this gateway runs no tools of its own. On the other providers the request is passed through untouched instead, so a tool the provider rejects comes back as that provider's own error. See Streaming for the delta shape.

JSON mode. response_format (for example {"type": "json_object"} or a json_schema) is passed to OpenAI-compatible providers unchanged, so it works wherever the provider supports it. On every model served by Bedrock (Claude, Nova, GPT-OSS, Qwen and others) and on Claude via Vertex it is currently ignored: the request succeeds, but the answer is not guaranteed to be JSON. For structured output there, use a tool with a JSON Schema in parameters and read the arguments of the tool call.

Reasoning models take a reasoning object and answer with message.reasoning and/or message.reasoning_details. Neither field is guaranteed: reasoning.exclude: true removes both, and thinking that a provider only returns encrypted arrives as a reasoning_details entry with no message.reasoning at all, because there is no plaintext. Treat both as optional. See Reasoning.

Responses

The same models, OpenAI's Responses shape. input takes a string or a list of input items, max_output_tokens replaces max_tokens, and the reply is a response object whose output array holds reasoning and message items.

bash
curl https://api.kontinent.ai/v1/responses \
  -H "Authorization: Bearer $KONTINENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"bedrock/claude-sonnet-5","input":"Hallo!",
       "reasoning":{"effort":"high"}}'

With "stream": true you get named events, each with a monotonic sequence_number: response.created, response.output_item.added, response.reasoning_text.delta, response.reasoning_text.done, response.content_part.added, response.output_text.delta, response.output_text.done, response.content_part.done, response.function_call_arguments.delta, response.function_call_arguments.done, response.output_item.done, response.completed, or response.failed when a generation breaks after the first byte. Item and content-part events are paired (added/done), so a client can close what it opened.

Tools work here too, in the flattened Responses shape: tools: [{"type": "function", "name": "...", "parameters": {...}}], with tool_choice and parallel_tool_calls. A call comes back as a function_call output item (call_id, name, arguments), and the loop continues by replaying it in input as a function_call item followed by a function_call_output item with the same call_id. On a reasoning model, replay the reasoning output item that preceded the call as well, reasoning_details and all: Claude verifies its own thinking and rejects a tool loop that comes back without it. The refusals are the ones chat completions make, named the same way.

Input items carry text only: an image or file content part returns 400. Fields this endpoint does not translate, such as text.format, are ignored rather than refused, so do not rely on them for structured output.

This endpoint is stateless. store: true and previous_response_id are rejected with 400. Kontinent retains no prompts or completions, so a stored response could never be read back. Send the previous turns in input instead.

Embeddings

bash
curl https://api.kontinent.ai/v1/embeddings \
  -H "Authorization: Bearer $KONTINENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"mistral/mistral-embed","input":"Hallo!"}'

Other OpenAI fields such as encoding_format and dimensions are passed to the provider unchanged; whether they take effect depends on the model. stream: true returns 400, because embeddings have no incremental form. Sending a chat model here (or an embeddings model to the chat endpoint) is rejected.

Audio

Speech to text and text to speech use the OpenAI audio shapes. Send a transcription model to the first endpoint and a speech model to the second; the wrong kind is rejected.

Transcription takes multipart/form-data with file and model (both required), plus optional language, prompt, temperature, response_format and user. The file may be up to 25 MB (the whole request up to 26 MB). response_format is json (default) or text; anything else returns 400, and so does stream=true. The file name is never forwarded to the provider, only a safe extension. The whole upload must finish within 5 minutes; a slower upload is cut off and returns 400. If this gateway instance is already busy with 4 transcriptions and no slot frees up within 10 seconds, you get 503 transcriptions_busy with a Retry-After: 10 header: retry after that many seconds.

bash
curl https://api.kontinent.ai/v1/audio/transcriptions \
  -H "Authorization: Bearer $KONTINENT_API_KEY" \
  -F model=scaleway/whisper-large-v3 \
  -F file=@gespraech.m4a \
  -F language=de

Speech takes JSON with model, input (up to 4096 characters) and voice (all required), plus optional response_format (mp3 default, wav, opus, aac, flac, pcm) and user. stream is not supported and returns 400. The reply body is the audio itself.

bash
curl https://api.kontinent.ai/v1/audio/speech \
  -H "Authorization: Bearer $KONTINENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"mistral/voxtral-tts","input":"Good morning","voice":"en_paul_neutral"}' \
  --output answer.mp3
  • Billing. Transcription is billed per started second of audio, measured by the gateway, with a minimum of one second. Speech is billed per character of input, counted as Unicode characters, not bytes. Prices appear in /v1/models as audio_micro_eur_per_hour and character_micro_eur_per_million; a field is omitted when it is zero.
  • Fallback. Transcription falls back along the model's fallback chain like chat. Speech never falls back to another model on its own, because a different model speaks with a different voice. Routing policies you build yourself still work.
  • Voices. voice must be one of the voices the catalog lists for that model. Any other value is refused with 400 before the provider is called, and the error lists the valid voices. mistral/voxtral-tts currently offers English voices only. For German, use ovh/nvr-tts-de-de with the voice German-DE-Male-1, its only voice. That model accepts at most 1000 characters and returns only wav (16 kHz mono): send response_format wav or leave it out, in which case the reply is wav, not mp3.
  • Guardrails. Content guardrails (the prompt-injection scan) do not inspect audio. Provider blocks and zero-data-retention rules still apply.

List models

typescript
const models = await client.models.list();

Returns every active model, the same list for every key, each with kind, context_length (where known), sovereignty, pricing, and, for models that think, a reasoning object (supported_efforts, default_effort, supports_max_tokens, mandatory). See Models & sovereignty.

Health

bash
curl https://api.kontinent.ai/healthz

Unauthenticated liveness probe. Use it for uptime monitoring, not for model availability.

Limits & conventions Handling errors

…