Documentation
The API is OpenAI-compatible. If a client speaks the OpenAI protocol, it works here by changing the base URL.
Quickstart
Set two values — your base URL and your key — and send a request. Your key is created when you register and can be rotated from the console.
export ICEBERG_BASE_URL="https://iceberg-inference.pages.dev/v1"
export ICEBERG_API_KEY="sk-router-…"
curl "$ICEBERG_BASE_URL/chat/completions" \
-H "Authorization: Bearer $ICEBERG_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "glm-5.3-flash",
"messages": [{ "role": "user", "content": "Hello" }]
}'
Python
import os
from openai import OpenAI
client = OpenAI(
base_url="https://iceberg-inference.pages.dev/v1",
api_key=os.environ["ICEBERG_API_KEY"],
)
completion = client.chat.completions.create(
model="glm-5.3-flash",
messages=[{"role": "user", "content": "Hello"}],
)
print(completion.choices[0].message.content)
Authentication
Send your key as a Bearer token in the Authorization header, or as x-api-key. Keys are account-scoped; each request counts against the plan shared by the account.
| Header | Value |
|---|---|
Authorization | Bearer sk-router-… |
x-api-key | sk-router-… |
Never ship a key in client-side code. Proxy requests through your own server and keep the key in an environment variable.
Chat completions
POST /v1/chat/completions accepts the standard OpenAI chat body — model, messages, temperature, max_tokens, tools, stream and the rest — and returns the same response shape.
| Field | Type | Notes |
|---|---|---|
model | string | Any id from the catalogue. |
messages | array | Standard role/content objects. |
temperature | number | Passed through to the provider. |
max_tokens | integer | Upper bound on the completion. |
stream | boolean | Server-sent events when true. |
Streaming
Send "stream": true and read the response as server-sent events. Failover is preserved mid-stream where the provider supports it; if the first provider drops before any token is sent, the router retries transparently.
const stream = await client.chat.completions.create({
model: "glm-5.3-flash",
messages: [{ role: "user", content: "Tell me a story." }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
Embeddings
POST /v1/embeddings mirrors the OpenAI embeddings request and returns vectors from the first embedding-capable provider for the model.
Images
POST /v1/images/generations and POST /v1/images/edits are supported. Edits accept multipart/form-data and are passed through untouched.
Audio
POST /v1/audio/speech returns generated audio, and POST /v1/audio/transcriptions accepts an uploaded file for transcription. Both keep the OpenAI request shape.
List models
GET /v1/models returns the catalogue in the OpenAI list format. GET /v1/models/:id returns one model with its capabilities.
{
"object": "list",
"data": [
{ "id": "glm-5.3-flash", "object": "model", "owned_by": "router" }
]
}
Errors
Errors use the OpenAI envelope. The type and code fields tell you what happened, and limit errors add limit, used and an upgrade_url.
{
"error": {
"message": "Monthly request limit reached (2000 on the Free plan).",
"type": "rate_limit_error",
"code": "monthly_request_limit",
"limit": 2000,
"used": 2000,
"upgrade_url": "https://iceberg-inference.pages.dev/pricing"
}
}
| Status | Meaning |
|---|---|
401 | Missing, invalid or disabled key. |
404 | Unknown model, or no provider available for it. |
429 | Plan or per-minute limit reached. Honour retry-after. |
502/503 | Every provider for the model failed. Retry shortly. |
Rate limits
Every plan carries a monthly request allowance, a monthly token allowance and a per-minute request rate. See pricing for the numbers, and your console for live usage against them.