Chat completions
POST https://api.entrim.ai/v1/chat/completions
Generates the next assistant message for a conversation. Requests and responses use the OpenAI chat completions format, so the official OpenAI SDKs and most OpenAI-compatible libraries work once you set the base URL to https://api.entrim.ai/v1.
Headers
| Header | Value |
|---|---|
Authorization | Bearer <your-api-key> |
Content-Type | application/json |
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
model | string | Yes | The model ID, for example zai-org/GLM-5.3-Flash. See List models. |
messages | array | Yes | The conversation so far, oldest message first. See Messages. |
max_tokens | integer | No | The most tokens to generate. On reasoning models this includes the reasoning. |
max_completion_tokens | integer | No | Same as max_tokens, under the newer OpenAI name. |
temperature | number | No | Sampling temperature between 0 and 2. Higher values give more varied output. |
top_p | number | No | Nucleus sampling: only tokens within this cumulative probability are considered. |
stop | string or array | No | One or more sequences that end the response when generated. |
seed | integer | No | Makes sampling repeatable on a best-effort basis. |
presence_penalty | number | No | Between -2.0 and 2.0. Positive values push the model toward new topics. |
frequency_penalty | number | No | Between -2.0 and 2.0. Positive values reduce repetition. |
reasoning_effort | string | No | low, medium or high. How much a reasoning model thinks before it answers. See Reasoning models. |
response_format | object | No | Ask for JSON output. See Structured output. |
tools | array | No | Functions the model may call. See Tool calling. |
tool_choice | string | No | auto (the default when tools is set), none or required. |
stream | boolean | No | Send the response as server-sent events while it's generated. See Streaming. |
stream_options | object | No | {"include_usage": true} adds a final chunk with token usage to the stream. |
logprobs | boolean | No | Return the log probability of each output token. |
top_logprobs | integer | No | How many of the most likely alternatives to return for each token. Requires logprobs. |
Messages
Each item in messages is an object with a role and content:
| Field | Type | Description |
|---|---|---|
role | string | system, user, assistant or tool |
content | string or array | The message text. User messages can also be an array of content parts, to mix text and images. |
tool_calls | array | Assistant messages only: the tool calls from an earlier response, sent back unchanged |
tool_call_id | string | Tool messages only: the id of the tool call this message answers |
"messages": [
{"role": "system", "content": "You are a concise assistant."},
{"role": "user", "content": "What is the capital of Slovenia?"},
{"role": "assistant", "content": "Ljubljana."},
{"role": "user", "content": "How many people live there?"}
]
Example request
- curl
- Python
- TypeScript
curl https://api.entrim.ai/v1/chat/completions \
-H "Authorization: Bearer $ENTRIM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "zai-org/GLM-5.3-Flash",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Write a haiku about the Julian Alps."}
]
}'
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.entrim.ai/v1",
api_key=os.environ["ENTRIM_API_KEY"],
)
response = client.chat.completions.create(
model="zai-org/GLM-5.3-Flash",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Write a haiku about the Julian Alps."},
],
)
print(response.choices[0].message.content)
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.entrim.ai/v1",
apiKey: process.env.ENTRIM_API_KEY,
});
const response = await client.chat.completions.create({
model: "zai-org/GLM-5.3-Flash",
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "Write a haiku about the Julian Alps." },
],
});
console.log(response.choices[0].message.content);
Example response
{
"id": "97bc062ce4f84e6c8e682b24ce4948dd",
"object": "chat.completion",
"created": 1790160983,
"model": "zai-org/GLM-5.3-Flash",
"choices": [
{
"index": 0,
"finish_reason": "stop",
"message": {
"role": "assistant",
"content": "Triglav wears her snow,\nthe Soča runs emerald\nthrough stone and silence.",
"reasoning_content": "The user wants a haiku about the Julian Alps. A haiku is a traditional Japanese poem with..."
}
}
],
"usage": {
"prompt_tokens": 28,
"completion_tokens": 841,
"total_tokens": 869,
"completion_tokens_details": {
"reasoning_tokens": 820,
"text_tokens": 21
}
}
}
The reasoning is shortened here. Notice that most of the completion tokens went to reasoning, which is typical for reasoning models.
| Field | Description |
|---|---|
choices[].message.content | The answer |
choices[].message.reasoning_content | The model's reasoning, on reasoning models |
choices[].message.tool_calls | Tool calls the model wants you to run, when you sent tools |
choices[].finish_reason | stop when the model finished, length when it hit max_tokens, tool_calls when it's waiting for tool results |
usage | Token counts for the request. completion_tokens includes the tokens in completion_tokens_details.reasoning_tokens. |
Streaming
With "stream": true, the response arrives as server-sent events. Each event is a data: line with a chat.completion.chunk object, and the stream ends with data: [DONE]. Text arrives in choices[0].delta.content, and on reasoning models the reasoning streams first in choices[0].delta.reasoning_content.
data: {"id":"6d1a18cc...","object":"chat.completion.chunk","model":"zai-org/GLM-5.3-Flash","choices":[{"index":0,"delta":{"reasoning_content":"The user"}}]}
data: {"id":"6d1a18cc...","object":"chat.completion.chunk","model":"zai-org/GLM-5.3-Flash","choices":[{"index":0,"delta":{"content":"1, 2, 3"}}]}
data: {"id":"6d1a18cc...","object":"chat.completion.chunk","model":"zai-org/GLM-5.3-Flash","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: {"id":"6d1a18cc...","object":"chat.completion.chunk","model":"zai-org/GLM-5.3-Flash","choices":[{"index":0,"delta":{}}],"usage":{"prompt_tokens":23,"completion_tokens":85,"total_tokens":108}}
data: [DONE]
The chunk with usage is only sent when you set "stream_options": {"include_usage": true}.
- curl
- Python
- TypeScript
curl -sN https://api.entrim.ai/v1/chat/completions \
-H "Authorization: Bearer $ENTRIM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "zai-org/GLM-5.3-Flash",
"messages": [{"role": "user", "content": "Write a haiku about the Julian Alps."}],
"stream": true,
"stream_options": {"include_usage": true}
}'
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.entrim.ai/v1",
api_key=os.environ["ENTRIM_API_KEY"],
)
stream = client.chat.completions.create(
model="zai-org/GLM-5.3-Flash",
messages=[{"role": "user", "content": "Write a haiku about the Julian Alps."}],
stream=True,
stream_options={"include_usage": True},
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
if chunk.usage:
print(f"\n\n{chunk.usage.total_tokens} tokens")
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.entrim.ai/v1",
apiKey: process.env.ENTRIM_API_KEY,
});
const stream = await client.chat.completions.create({
model: "zai-org/GLM-5.3-Flash",
messages: [{ role: "user", content: "Write a haiku about the Julian Alps." }],
stream: true,
stream_options: { include_usage: true },
});
for await (const chunk of stream) {
const text = chunk.choices[0]?.delta?.content;
if (text) process.stdout.write(text);
if (chunk.usage) console.log(`\n\n${chunk.usage.total_tokens} tokens`);
}
Tool calling
Tool calling lets the model ask your code to run a function. Describe each function with a JSON Schema in tools:
{
"model": "zai-org/GLM-5.3-Flash",
"messages": [{"role": "user", "content": "What is the weather in Ljubljana right now?"}],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name"}
},
"required": ["city"]
}
}
}
]
}
When the model decides to use a tool, the response has finish_reason set to tool_calls, and the message lists the calls. arguments is a JSON-encoded string:
"message": {
"role": "assistant",
"content": "",
"tool_calls": [
{
"id": "call_4f81571db0a24b629241aa97",
"type": "function",
"function": {"name": "get_weather", "arguments": "{\"city\": \"Ljubljana\"}"}
}
]
}
Run the function, then send the conversation back with the assistant message and one tool message per call. Keep the same tools in the request:
"messages": [
{"role": "user", "content": "What is the weather in Ljubljana right now?"},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"id": "call_4f81571db0a24b629241aa97",
"type": "function",
"function": {"name": "get_weather", "arguments": "{\"city\": \"Ljubljana\"}"}
}
]
},
{
"role": "tool",
"tool_call_id": "call_4f81571db0a24b629241aa97",
"content": "{\"temperature_c\": 18, \"conditions\": \"partly cloudy\"}"
}
]
The model then answers using the result, for example: "It's partly cloudy in Ljubljana at 18 °C."
Set tool_choice to required to make the model call a tool, or to none to stop it from calling one.
Images
Models that accept images take them as content parts in a user message, next to the text. Pass an https:// URL that the API can download, or embed the image as a base64 data URL:
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "What does this image show?"},
{"type": "image_url", "image_url": {"url": "https://entrim.ai/apple-touch-icon.png"}}
]
}
]
{"type": "image_url", "image_url": {"url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."}}
Images count toward the prompt tokens. The model library shows which models accept images.
Structured output
To get JSON back, set response_format. json_object asks for any valid JSON object, so describe the shape you want in the prompt:
"response_format": {"type": "json_object"}
json_schema constrains the output to a schema:
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "city",
"strict": true,
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"country": {"type": "string"}
},
"required": ["name", "country"],
"additionalProperties": false
}
}
}
The JSON arrives as a string in message.content, so parse it in your code.
Errors
Errors return an HTTP status code and an error object:
{
"error": {
"message": "Authentication Error, No api key passed in.",
"type": "auth_error",
"param": "None",
"code": "401"
}
}
| Status | Meaning | What to do |
|---|---|---|
400 | The request is invalid, for example an unknown model ID | Check the message, fix the request and don't retry it unchanged |
401 | The API key is missing or invalid | Check the Authorization header and the key in the dashboard |
429 | Too many requests | Wait and retry with exponential backoff |
5xx | Something went wrong on our side | Retry with exponential backoff |