Skip to content
Documentation menu

API reference

Chat.

Find a model, then talk to it: one answer or a stream, with tool calls and JSON output when the model supports them.

GET/v1/models

List models

The online models (LLMs) this key can call. Send the returned id as model, exactly as it is.

Example

curl https://api.ompera.net/v1/models \
  -H "Authorization: Bearer $OMPERA_API_KEY"

Response

{
  "object": "list",
  "data": [
    {
      "id": "my/local",
      "object": "model",
      "owned_by": "ompera-owner",
      "name": "Qwen3 4B",
      "kind": "chat",
      "capabilities": ["chat", "streaming", "tools", "json_object", "json_schema"],
      "available_nodes": 1,
      "capacity": 1,
      "inflight": 0
    }
  ]
}

One entry per model. Pool models also carry pool and copies.

FieldMeaning
idThe address to send as model.
owned_byompera-owner: your own model (my/…). ompera-pool: a model from a member of your pool (<pool>/…). ompera-community: an Ompera network model, available to every key.
capabilitiesFeatures the model passed: streaming, tools, json_object, json_schema. A request that needs a missing one is refused with unsupported_model_capability.
pool, copiesPool models only: the pool, and how many members run the model now. Ompera sends each request to a free copy.
capacity, inflightHow many requests the model can run at once, and how many are running now.
Why a model may be missing

Only online models are listed, and the key must allow them. Models connected from a computer (my/… and pool models) need a key with Allow Unshielded models. If your model is offline, its address still works: requests fail with provider_unavailable unless the key's offline setting sends them to a pool model (offline settings).

POST/v1/chat/completions

Chat completions

Send the conversation, get the assistant's message. The standard OpenAI request body.

Request

FieldNotes
modelrequiredAn id from List models.
messagesrequiredThe conversation: {"role", "content"} objects with the roles system, user, assistant and tool.
streamtrue sends the answer while it is written. See Streaming.
tools, tool_choiceFunctions the model may call. See Tool calls.
response_formattext, json_object or json_schema. See JSON output.
Anything elsetemperature, max_tokens, top_p, stop, seed and other fields reach the model server unchanged; what they do depends on that server.

Example

curl

curl https://api.ompera.net/v1/chat/completions \
  -H "Authorization: Bearer $OMPERA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "MODEL_ADDRESS",
    "messages": [
      {"role": "system", "content": "You are a helpful assistant."},
      {"role": "user", "content": "Hello!"}
    ]
  }'

Python

import os
from openai import OpenAI

client = OpenAI(base_url="https://api.ompera.net/v1", api_key=os.environ["OMPERA_API_KEY"])
reply = client.chat.completions.create(
    model="MODEL_ADDRESS",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Hello!"},
    ],
)
print(reply.choices[0].message.content)

JavaScript

import OpenAI from "openai";

const client = new OpenAI({ baseURL: "https://api.ompera.net/v1", apiKey: process.env.OMPERA_API_KEY });
const reply = await client.chat.completions.create({
  model: "MODEL_ADDRESS",
  messages: [
    { role: "system", content: "You are a helpful assistant." },
    { role: "user", content: "Hello!" },
  ],
});
console.log(reply.choices[0].message.content);

Response

{
  "id": "chatcmpl-8f2c",
  "object": "chat.completion",
  "model": "my/local",
  "choices": [
    {
      "index": 0,
      "message": {"role": "assistant", "content": "Hello! How can I help?"},
      "finish_reason": "stop"
    }
  ],
  "usage": {"prompt_tokens": 21, "completion_tokens": 7, "total_tokens": 28}
}
REQUESTmessagesYour conversation goes in.
RESPONSEchoices[0].messageThe assistant’s message comes back.
TEXTmessage.contentRead the returned text.

The body comes from the model server; model is set to the address that answered, and usage is there when the server reports it. The X-Ompera-Model header carries the same address. It differs from the one you sent only when the key's offline setting switched to another pool model.

Streaming

Set "stream": true to receive the answer as server-sent events while the model writes it. The model needs the streaming capability.

Python

import os
from openai import OpenAI

client = OpenAI(base_url="https://api.ompera.net/v1", api_key=os.environ["OMPERA_API_KEY"])
stream = client.chat.completions.create(
    model="MODEL_ADDRESS",
    messages=[{"role": "user", "content": "Write a haiku about the sea."}],
    stream=True,
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)

JavaScript

import OpenAI from "openai";

const client = new OpenAI({ baseURL: "https://api.ompera.net/v1", apiKey: process.env.OMPERA_API_KEY });
const stream = await client.chat.completions.create({
  model: "MODEL_ADDRESS",
  messages: [{ role: "user", content: "Write a haiku about the sea." }],
  stream: true,
});
for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}

curl

curl -N https://api.ompera.net/v1/chat/completions \
  -H "Authorization: Bearer $OMPERA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"MODEL_ADDRESS","stream":true,"messages":[{"role":"user","content":"Write a haiku about the sea."}]}'
OPENHTTP 200The stream is open, not yet complete.
RECEIVEdeltaAppend each piece of content or tool call.
FINISH[DONE]The answer is complete.
What the events look like
data: {"object":"chat.completion.chunk","model":"my/local","choices":[{"index":0,"delta":{"role":"assistant","content":"Waves"}}]}

data: {"object":"chat.completion.chunk","model":"my/local","choices":[{"index":0,"delta":{"content":" fold into foam"}}]}

data: {"object":"chat.completion.chunk","model":"my/local","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: [DONE]

A chunk may have no content, or an empty choices array when it only carries usage.

When the model fails mid-stream

The status is already 200, so the error arrives as an event, followed by [DONE]. The OpenAI SDKs raise it as an API error; with raw SSE, check each event for error and treat a stream without finish_reason as incomplete.

data: {"error":{"code":"provider_protocol_error","message":"provider returned an invalid streaming response"}}

data: [DONE]

Tool calls

Describe functions in tools; the model answers with tool_calls when it wants one run. The model needs the tools capability.

01 · ASKSend tool definitionsYour code sends tools and messages.
02 · ACTRun the toolYour code runs what the model asks for. Ompera never runs tools.
03 · REPLYSend the resultThe model uses it to continue.
import json, os
from openai import OpenAI

client = OpenAI(base_url="https://api.ompera.net/v1", api_key=os.environ["OMPERA_API_KEY"])
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Current weather in a city",
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
        },
    },
}]
messages = [{"role": "user", "content": "What's the weather in Lisbon?"}]

while True:
    reply = client.chat.completions.create(model="MODEL_ADDRESS", messages=messages, tools=tools)
    message = reply.choices[0].message
    if not message.tool_calls:
        break
    messages.append(message)
    for call in message.tool_calls:
        arguments = json.loads(call.function.arguments)
        result = {"city": arguments["city"], "temperature_c": 21}  # call your real function here
        messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)})

print(message.content)

When streaming, tool calls arrive in pieces: join delta.tool_calls by index before parsing the arguments.

JSON output

json_schema asks the model to follow your schema; json_object asks only for valid JSON, so describe the shape in the prompt. They are separate capabilities: check the model's capabilities first.

import json, os
from openai import OpenAI

client = OpenAI(base_url="https://api.ompera.net/v1", api_key=os.environ["OMPERA_API_KEY"])
reply = client.chat.completions.create(
    model="MODEL_ADDRESS",
    messages=[{"role": "user", "content": "Anna is 31 and lives in Porto."}],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "person",
            "schema": {
                "type": "object",
                "properties": {"name": {"type": "string"}, "age": {"type": "integer"}, "city": {"type": "string"}},
                "required": ["name", "age", "city"],
            },
        },
    },
)
person = json.loads(reply.choices[0].message.content)

Small models can still return invalid JSON: validate the result, and retry or fall back if parsing fails.