Overview
siBoi exposes every model in the catalog through one OpenAI-compatible API. One key, one base URL, any model ID from the list below.
- Chat Completions: text, reasoning, image input, tool calling and SSE streaming.
- Responses, Anthropic Messages and Gemini generateContent are accepted and translated server side.
- Usage is reported per request: input and output tokens, then charged with the model multiplier.
Quick start
Get a key from the console, set the base URL, send your first request.
curl -sS https://api.siboi.dev/v1/chat/completions \
-H "Authorization: Bearer $SIBOI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4.1-flash-1017",
"messages": [{"role": "user", "content": "Halo"}],
"max_tokens": 256
}'
from openai import OpenAI
client = OpenAI(
base_url="https://api.siboi.dev/v1",
api_key="si-boi-...",
)
r = client.chat.completions.create(
model="deepseek-v4.1-flash-1017",
messages=[{"role": "user", "content": "Halo"}],
max_tokens=256,
)
print(r.choices[0].message.content)
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.siboi.dev/v1",
apiKey: process.env.SIBOI_API_KEY,
});
const r = await client.chat.completions.create({
model: "deepseek-v4.1-flash-1017",
messages: [{ role: "user", content: "Halo" }],
max_tokens: 256,
});
console.log(r.choices[0].message.content);
The examples use the model IDs shown in the Models section. Every ID is also available from GET /v1/models.
Authentication
Send the key in the Authorization header on every request. Keys look like si-boi-... for token plans and si-boi-unli-... for unlimited plans.
Authorization: Bearer $SIBOI_API_KEY
Content-Type: application/json
| Header | Value |
|---|---|
Authorization | Bearer plus your API key. Required. |
Content-Type | application/json. Required for POST. |
Accept | text/event-stream for streaming, JSON by default. |
Endpoints
| Endpoint | Function |
|---|---|
POST /v1/chat/completions | Chat, reasoning, images, tools, streaming. |
GET /v1/models | Model IDs available to your key. |
POST /v1/responses | OpenAI Responses format, translated server side. |
POST /v1/messages | Anthropic Messages format. |
POST /v1beta/models/{model}:generateContent | Gemini generateContent format. |
All inference endpoints share the same base URL. Do not append /v1 twice in an SDK.
Models and limits
The table is generated live from this gateway, so it always matches what your key can call. Context and max output come from the provider metadata; a dash means the provider does not report it.
| Model | ID | Context | Max output | Multiplier | Capability |
|---|---|---|---|---|---|
| Loading models... | |||||
Quota is charged as (input tokens + output tokens) x multiplier. A 2x model consumes twice the quota of a 1x model for the same request.
Chat completions
A minimal request needs model and messages. The answer is in choices[0].message.content and token usage is in usage.
{
"model": "deepseek-v4.1-flash-1017",
"messages": [
{"role": "system", "content": "Answer briefly."},
{"role": "user", "content": "What is SSE?"}
],
"temperature": 0.7,
"max_tokens": 256
}
Common parameters
| Parameter | Type | Description |
|---|---|---|
model | string | Required. Any ID from the Models table. |
messages | array | Required. Roles: system, user, assistant, tool. |
max_tokens | integer | Output limit. max_completion_tokens is accepted too. Values above your key cap are clamped. |
temperature | number | 0 - 2. |
top_p | number | 0 - 1. |
stream | boolean | Return Server-Sent Events instead of JSON. |
stop | string / array | Stop sequence. |
tools | array | Function definitions for tool calling. |
tool_choice | string / object | auto, none, required or a named function. |
response_format | object | json_object or json_schema. |
Streaming
Set stream to true. The response is text/event-stream, each event carries data: {...} and the stream ends with data: [DONE].
curl -N -sS https://api.siboi.dev/v1/chat/completions \
-H "Authorization: Bearer $SIBOI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4.1-flash-1017",
"messages": [{"role": "user", "content": "Write one paragraph."}],
"stream": true,
"max_tokens": 512
}'
Reasoning models may emit delta.reasoning_content before the answer arrives in delta.content. Tool call arguments arrive in fragments: join them by index before parsing.
Tool calling
Define the function in tools, let the model call it, then send the result back with role tool and the matching tool_call_id.
{
"model": "deepseek-v4.1-flash-1017",
"messages": [{"role": "user", "content": "Look up order A-102."}],
"tools": [{
"type": "function",
"function": {
"name": "lookup",
"description": "Find an order by id",
"parameters": {
"type": "object",
"properties": {"id": {"type": "string"}},
"required": ["id"]
}
}
}],
"tool_choice": "auto",
"max_tokens": 512
}
{
"role": "tool",
"tool_call_id": "call_abc123",
"content": "{\"status\":\"paid\"}"
}
Not every model supports tools. Check the Capability column in the Models table.
Structured output
Use response_format. Always validate the JSON in your app and check finish_reason before trusting the payload.
{
"model": "deepseek-v4.1-flash-1017",
"messages": [{"role": "user", "content": "Return {\"ok\": true}."}],
"response_format": {"type": "json_object"},
"max_tokens": 512
}
Reasoning tokens are counted inside max_tokens, so leave headroom. A too small budget can return finish_reason length with empty content and HTTP 200.
Other request formats
Point an existing SDK at the base URL and call the format it already speaks. The gateway translates to Chat Completions internally.
| Format | Path | Notes |
|---|---|---|
| OpenAI Chat | /v1/chat/completions | Native, streaming supported. |
| OpenAI Responses | /v1/responses | Stateless: send full history every call. Non streaming. |
| Anthropic Messages | /v1/messages | system and text content blocks are mapped. |
| Gemini | /v1beta/models/{model}:generateContent | contents plus systemInstruction. |
Quota and limits
| Mechanism | Behavior |
|---|---|
| Token quota | Token plans have a token quota. Charged as (input + output) x multiplier. When it runs out the API replies 402 until you top up. |
| Fair use | Unlimited plans use a request cap instead of a token quota. Hitting it returns 429. |
| RPM | Requests per minute per key. Exceeding it returns 429 rate_error. |
| Output cap | Each key has a max output per request. Higher max_tokens values are clamped, not rejected. |
| Key expiry | Every key has an active period. An expired key returns 401 auth_error. |
Errors
Errors use {"error": {"message", "type"}} and keep the HTTP status of the cause.
| HTTP | Type | Cause and action |
|---|---|---|
| 401 | auth_error | Missing, wrong or expired key. Create a new key in the console. |
| 402 | quota_error | Token quota exhausted. Top up, the key stays the same. |
| 403 | model_error | The key is not allowed to use that model ID. Check the key model list. |
| 429 | rate_error | RPM limit reached. Wait and retry. |
| 429 | quota_error | Fair use limit of an unlimited plan reached. |
| 502 | proxy_error | The model provider did not respond. Retry the request. |
Upstream rejections are forwarded with their own status and a message of the form Upstream returned N.
Troubleshooting
401 even though the key looks correct
Check for trailing spaces or a missing Bearer prefix, and confirm the key is still active and not expired in the console.
HTTP 200 but empty content
The model spent the whole budget on reasoning. Raise max_tokens, typically 512 to 1024 for reasoning models.
402 in the middle of the month
The token quota is charged with the model multiplier, so heavy models drain it faster. Top up or switch to a lower multiplier model.
Model ID rejected
Use the exact ID from the Models table above or from GET /v1/models. IDs are case sensitive.
Tool arguments arrive incomplete
Streaming sends tool arguments in fragments. Concatenate them by index, then parse once finish_reason is tool_calls.