Chat Completions API
OpenAI-compatible POST /v1/chat/completions with multiple models, provider routing, streaming, and tool calls.
Inference Space provides the OpenAI-compatible chat completion endpoint POST /v1/chat/completions across the LLM model families in the catalog, including Claude, GPT, Gemini, DeepSeek, Qwen, and Kimi. Existing OpenAI-compatible clients can connect by changing only the Base, Key, and model; no application-code rewrite is required.
The private deployment API is identical; only the Base domain needs to change → Enterprise private deployment.
Overview
- Endpoint:
POST https://ai.inf.space/v1/chat/completions - Base:
https://ai.inf.space/v1(the browser console shares theai.inf.spacehost; use the/v1path for API requests) - Protocol: OpenAI Chat Completions compatible, with request and response fields matching OpenAI
- Multiple models: Select an upstream with
model+providerusing the same Key and endpoint
Actual model IDs and prices depend on what is shown in the console. Manage model IDs as configuration so that versions can be changed smoothly as the console is updated. See Model list and Authentication.
Authentication
Authenticate with the HTTP header Authorization: Bearer <gk_...>. API Keys start with gk_ and are created in the console. The request body is application/json.
Authorization: Bearer gk_xxxxxxxxxxxxxxxx
Content-Type: application/jsonIn production, keep the Key on the server and do not expose it to browsers or clients.
Provider is required
Inference Space uses strict routing: every request must explicitly specify an upstream provider, with no implicit fallback. Use one of these three declaration methods, in descending priority:
- URL query parameter
?provider=X(required for OpenRouter because itsbody.providerfield is preserved as an upstream routing object) - Request header
X-Provider: X(useful for clients such as sandbox CLIs that cannot append a query string tobase_url) - Request body field
body.provider
If the provider is missing or unknown, the API returns 400 with an OpenAI-style invalid_request_error body:
{
"error": {
"message": "'provider' is required: pass it via ?provider=X or body.provider (valid: anthropic, deepseek, ...)",
"type": "invalid_request_error"
}
}There is no default provider. Even when model maps uniquely to an upstream, you must still declare the provider explicitly; otherwise SaaS-only models return 400, requiring the client to express its intent. /v1/chat/completions and /v1/responses share this behavior; only /v1/messages defaults to anthropic when omitted.
Routable LLM providers
The publicly routable providers are:
| provider | Description |
|---|---|
anthropic | Claude family (meganova, packy, and other sources are presented externally as anthropic) |
openai | GPT family |
dashscope | Qwen (Alibaba Cloud Model Studio) |
deepseek | DeepSeek |
gemini | Google Gemini |
kimi | Kimi (Moonshot AI) |
openrouter | OpenRouter aggregation routing |
Available providers and models depend on console settings and organization authorization.
Gemini models
Route Google Gemini with provider=gemini. Model IDs match Google's official IDs; requests and responses pass through the OpenAI-compatible protocol unchanged:
| model | Context window | Vision | Description |
|---|---|---|---|
gemini-3.5-flash | 1,048,576 | ✅ | Cost-effective Flash with strong coding and agent capabilities (2026-05 GA) |
gemini-3.1-pro-preview | 1,048,576 | ✅ | Flagship multimodal reasoning (text / image / video / audio / code) |
gemini-2.0-flash | 1,048,576 | ✅ | Previous-generation Flash |
gemini-1.5-pro | 2,097,152 | ✅ | Pro with an extra-long context window |
Gemini 3.x models use reasoning (thinking) tokens before producing an answer. Set max_tokens large enough (we recommend ≥ 1024); a budget that is too small can consume the entire allowance on thinking tokens, causing message.content to return null with finish_reason set to length.
Basic request
curl "https://ai.inf.space/v1/chat/completions?provider=anthropic" \
-H "Authorization: Bearer $TOS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-x",
"messages": [
{ "role": "system", "content": "You are a concise assistant." },
{ "role": "user", "content": "Introduce yourself in three sentences." }
]
}'import os
from openai import OpenAI
client = OpenAI(
base_url="https://ai.inf.space/v1",
api_key=os.environ["TOS_API_KEY"],
)
resp = client.chat.completions.create(
model="claude-sonnet-4-x",
messages=[
{"role": "system", "content": "You are a concise assistant."},
{"role": "user", "content": "Introduce yourself in three sentences."},
],
extra_query={"provider": "anthropic"},
)
print(resp.choices[0].message.content)import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://ai.inf.space/v1",
apiKey: process.env.TOS_API_KEY,
});
const resp = await client.chat.completions.create(
{
model: "claude-sonnet-4-x",
messages: [
{ role: "system", content: "You are a concise assistant." },
{ role: "user", content: "Introduce yourself in three sentences." },
],
},
{ query: { provider: "anthropic" } },
);
console.log(resp.choices[0].message.content);The official OpenAI SDK has no top-level provider field. We recommend the ?provider= query parameter (Python extra_query / Node query) or adding X-Provider to the default headers. Raw fetch or curl requests can also put provider in the JSON body.
Parameters
The standard OpenAI fields are supported:
| Parameter | Type | Required | Description |
|---|---|---|---|
model | string | Yes | Model ID; use the value shown in the console. |
messages | array | Yes | Array of conversation messages; each item contains role and content. |
max_tokens | integer | No | Maximum number of output tokens in a response. |
temperature | number | No | Sampling temperature; higher values produce more varied output. |
top_p | number | No | Nucleus-sampling threshold; use it instead of temperature. |
stop | string / array | No | Stop sequence; generation stops when it is matched. |
stream | boolean | No | Whether to return an SSE stream; defaults to false. |
tools | array | No | List of tool (function) definitions. |
tool_choice | string / object | No | Tool-selection strategy, such as "auto" or a specific tool. |
Parameter support varies by provider and model. The gateway removes fields unsupported by the (provider, model) pair before forwarding the request, avoiding upstream 400 errors. body.provider is the gateway routing key and is not sent upstream for non-OpenRouter providers. Only OpenRouter's body.provider (an upstream routing object such as { "order": [...], "allow_fallbacks": true }) is forwarded unchanged.
Streaming
With stream: true, the response is returned as Server-Sent Events (SSE) chunks. Each chunk is the OpenAI-standard chat.completion.chunk; incremental text is in choices[0].delta.content, and the stream ends with data: [DONE].
curl "https://ai.inf.space/v1/chat/completions?provider=anthropic" \
-H "Authorization: Bearer $TOS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-x",
"stream": true,
"messages": [
{ "role": "user", "content": "Write a short poem about clouds." }
]
}'For streaming requests, the gateway forcibly injects stream_options.include_usage = true so the upstream sends a usage chunk at the end of the stream for metering and billing. The final chunk has an empty choices array ([]), which standard SDKs such as OpenAI, LangChain, LiteLLM, and Vercel AI SDK ignore automatically. Only hand-written SSE parsers that unconditionally access chunk.choices[0] need an empty-array check. Even if the client explicitly sends include_usage:false, the gateway overrides it to true; token metering is the gateway's responsibility and cannot be bypassed.
The final usage chunk looks like this:
{
"id": "chatcmpl-xxx",
"object": "chat.completion.chunk",
"choices": [],
"usage": {
"prompt_tokens": 24,
"completion_tokens": 38,
"total_tokens": 62
}
}Tool calls
Declare callable functions with tools. When needed, the model returns tool_calls. After the application executes a tool, add its result as a role: "tool" message in the next messages turn so the model can produce the final answer.
curl "https://ai.inf.space/v1/chat/completions?provider=anthropic" \
-H "Authorization: Bearer $TOS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-x",
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the weather for a specified city",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "City name" }
},
"required": ["city"]
}
}
}
],
"tool_choice": "auto",
"messages": [
{ "role": "user", "content": "What is the weather like in Hefei today?" }
]
}'Responses and usage
The non-streaming response uses the standard OpenAI structure:
{
"id": "chatcmpl-xxx",
"object": "chat.completion",
"model": "claude-sonnet-4-x",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "Hello, I am a concise assistant." },
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 24,
"completion_tokens": 38,
"total_tokens": 62
}
}Billing is based on input and output tokens in usage, in units of 1M tokens. Exact prices, available models, and discounts depend on console and organization pricing; organization-specific prices and contract discounts take precedence.