Skip to Content
Launching the open-source gateway

API formats

Every API the gateway serves to clients, its endpoints, and which vendors can answer it.

A format is one vendor’s API, such as OpenAI Chat Completions or Anthropic Messages. Your client calls the gateway in its own format. The gateway decodes the request into a neutral type, sends it to the provider in a format that provider speaks, and writes the answer back in the client’s format.

Formats of the same kind translate into each other. A chat client can reach any provider that speaks any chat format. An image client can reach any provider that speaks an image format. Formats of different kinds never mix.

Chat

FormatEndpointVendors that speak it
openai-chatPOST /v1/chat/completionsOpenAI, Gemini, Respan, Groq, DeepSeek, OpenRouter, xAI, Mistral, Together, Fireworks, Ollama, Azure, vLLM, Cloudflare, Cerebras
openai-responsesPOST /v1/responsesOpenAI, Respan, Perplexity
anthropic-messagesPOST /v1/messagesAnthropic, Respan
gemini-generatePOST /v1beta/models/{model}:generateContent, POST /v1beta/models/{model}:streamGenerateContentGemini, Respan

A client of any chat format can call a model on any of these vendors. For example, the Anthropic SDK can call gpt-6-luna, and the OpenAI SDK can call claude-haiku-5-5.

Translated between chat formats:

  • Streaming. A stream and a whole response are the same events inside the gateway, so a client can stream from a provider that answers whole, and the other way round. A Gemini provider is always asked for SSE.
  • Tools. Function tools, tool calls and tool results, including the tool choice where the target format has a form for it.
  • Images and documents in messages, by the target format’s rules.
  • Reasoning. Effort levels and token budgets map where the target has them (see the table below).
  • previous_response_id. A Responses client keeps it with any provider. The responses plugin stores the conversation in memory.

Reasoning and sampling settings:

SettingOpenAI ChatResponsesAnthropicGemini
Effort offreasoning_effort: "none"reasoning.effort: "none"thinking: {type: "disabled"}thinkingBudget: 0
Effort minimal to highreasoning_effortreasoning.effortoutput_config.effortthinkingLevel
Token budgetrefusedrefusedthinking.budget_tokensthinkingBudget
top_krefusedrefusedtop_kgenerationConfig.topK

Known gaps

Some fields have no equivalent in the target format yet. When a request carries one, the gateway doesn’t drop it silently. It answers 400 and names what it could not send, for example 'safetySettings' in the request cannot be translated to another format yet. Sent to a provider of the client’s own format, the same field passes through unchanged.

Examples of what is refused in translation today:

  • From a Responses client: include, truncation, metadata, reasoning settings other than an effort or an auto summary, and built-in tools (web search, file search, computer use, code interpreter, MCP).
  • From a Gemini client: safetySettings, cachedContent, Google-run tools such as googleSearch, and video parts.
  • Reasoning and sampling: a token budget or top_k sent to an OpenAI format, and an effort and a budget together to anything but Anthropic.

Coding agents such as Claude Code, Codex and Gemini CLI rely on vendor-specific fields (thinking, tool shapes, top_k and others). Some of these can’t be translated yet, so don’t expect these agents to work against another vendor’s models through the gateway. To put the gateway in front of Claude Code or Codex with their own vendor, use respan-gateway proxy.

Images

FormatEndpointVendors that speak it
openai-imagesPOST /v1/images/generations, POST /v1/images/editsOpenAI, xAI, Together
gemini-imagesPOST /v1beta/models/{model}:generateContentGemini

gemini-images shares its path with gemini-generate. A Gemini client’s call is treated as an image request only when its model routes to an image provider that can’t take generateContent, such as OpenAI. Gemini’s own image models are asked as chats, as Gemini clients expect.

Gemini draws only certain aspect ratios. A size is mapped to the nearest one within 5%, or refused with 400. A mask is refused.

Audio and embeddings

FormatEndpointVendors that speak it
openai-transcriptionsPOST /v1/audio/transcriptions, POST /v1/audio/translationsOpenAI, Groq, Mistral
openai-speechPOST /v1/audio/speechOpenAI, Groq, Mistral
openai-embeddingsPOST /v1/embeddingsOpenAI, Gemini, Mistral, Together, Fireworks, Ollama, Cloudflare

Streaming works as OpenAI’s API defines it: transcript.text.* events for transcriptions, speech.audio.* with stream_format: "sse" for speech. Audio and video bytes are relayed as they arrive.

Video

FormatEndpointVendors that speak it
openai-videosPOST /v1/videos, GET /v1/videos, GET /v1/videos/{id}, DELETE /v1/videos/{id}, GET /v1/videos/{id}/content, POST /v1/videos/{id}/remixnone directly; answered by Gemini’s Veo
gemini-veoPOST /v1beta/models/{model}:predictLongRunning, GET /v1beta/{operation}, GET /v1beta/files/{id}:downloadGemini

An OpenAI Videos client makes Veo videos: the gateway turns the job into a Veo operation and serves the content at its own URL, so the client never needs the Google key. Veo can’t remix, delete or list videos, or make thumbnails. Those requests are refused with 400 unsupported.

Realtime

These are WebSocket sessions.

FormatEndpointVendors that speak it
openai-realtimeGET /v1/realtime (WebSocket)OpenAI
gemini-liveGET /ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent (WebSocket)Gemini
mistral-realtimeGET /v1/audio/transcriptions/realtime (WebSocket)Mistral
responses-wsGET /v1/responses (WebSocket)OpenAI
  • OpenAI Realtime ⇄ Gemini Live. An OpenAI Realtime client can talk to a Gemini Live model, and a Gemini Live client to an OpenAI model. Audio is converted between the two (G.711 and PCM, 24 kHz). The model is read from ?model= on the handshake.
  • Mistral realtime transcription. Mistral only transcribes. It can answer an OpenAI Realtime transcription session (type: "transcription"), and a Mistral client can transcribe with OpenAI’s or Gemini’s models. A conversation session sent to Mistral is refused.
  • responses-ws is OpenAI’s Responses over a WebSocket. It is not translated, and it needs exactly one provider that speaks it.

Some settings have no equivalent on the other side. For example, Gemini fixes its setup when a session starts, so an OpenAI client’s session.update that changes anything closes the session with code 1008. Settings that don’t change the answer, such as noise reduction or VAD tuning, are dropped.

Decisions

FormatEndpointVendors that speak it
decisionsPOST /v1/decisions, POST /v1/systemoneRespan, TypeSafe

A typed decision request: {"model", "state", "questions"}, where each question is a noul, choice or score. The answer is {"model", "answers", "usage"}. Both paths take the same request; the gateway calls Respan at /decisions and TypeSafe at /systemone.

Tools (MCP)

EndpointWhat it serves
/mcpEvery tool server as one MCP server, tools named server__tool
/mcp/{name}One tool server, [tools.{name}], as it is

These use MCP’s streamable HTTP. They exist only when the config has tool servers. See Tools and MCP.

Other endpoints

EndpointWhat it does
GET /v1/modelsEvery provider’s models, in OpenAI’s list shape. Needs a client key when keys are set.
GET /v1beta/models, GET /v1beta/models/{model}The same list in Gemini’s shape, for Gemini SDKs.
GET /healthzAnswers ok. Needs no client key.
/ui/The web UI, and the admin API under /ui/api/. Off with ui = false.

respan-gateway proxy does not serve /v1/models or /v1beta/models; the vendor answers them.

Which formats are served

With no formats setting, the gateway serves:

  • openai-chat and anthropic-messages, always;
  • every other chat format that at least one provider speaks;
  • every image, audio, embedding, video, decision and realtime format whose kind at least one provider speaks;
  • responses-ws if a provider speaks it;
  • /mcp if there are tool servers.

So with only an Anthropic key, POST /v1/responses and Gemini’s generateContent are not served. List the formats you want at the top of gateway.toml to serve them anyway:

formats = ["openai-chat", "openai-responses", "anthropic-messages", "gemini-generate"]

One more format, passthrough, relays every request the other formats don’t take, unread, to the one provider that speaks passthrough. It must come last in formats. respan-gateway proxy uses it.

Which formats each vendor speaks, and its base URLs, is on Providers and models.