Skip to content
mini-lab

API reference

The mini-lab API is compatible with OpenAI's Chat Completions API. Anything that speaks it (the official SDKs, curl, LangChain…) works by changing the base URL:

https://mini-lab.sitesolide.fr/v1

Quickstart

Create a key on the API keys page, export it, and send a message:

export MINILAB_API_KEY=sk-mini-...

curl https://mini-lab.sitesolide.fr/v1/chat/completions \
  -H "Authorization: Bearer $MINILAB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "mini-1", "messages": [{"role": "user", "content": "Tell me a story about a cat"}]}'

With the official Python SDK (pip install openai):

import os
from openai import OpenAI

client = OpenAI(base_url="https://mini-lab.sitesolide.fr/v1", api_key=os.environ["MINILAB_API_KEY"])
response = client.chat.completions.create(
    model="mini-1",
    messages=[{"role": "user", "content": "Tell me a story about a cat"}],
)
print(response.choices[0].message.content)
print(response.usage)  # prompt_tokens, completion_tokens: what you are billed for

The models are tiny: they write short children's stories and do simple arithmetic (with a calculator). Don't expect more, that's the charm.

Authentication

Send your key as a bearer token: Authorization: Bearer sk-mini-.... Keys belong to a project inside an organization; every request is billed to that organization and logged with the key and project.

  • We store only a hash of each key, so a key is shown once, when you create it. Lost it? Revoke it and create a new one.
  • A revoked key is rejected immediately with 401 invalid_api_key.
  • A key can have a spend limit: once reached, it gets 429 insufficient_quota while other keys keep working.
  • Never put a key in client-side code: anyone who sees it can spend your credits.

Chat completions

POST /v1/chat/completions generates the next assistant message of a conversation.

ParameterTypeDescription
modelstring, requiredA model id from GET /v1/models, e.g. mini-1.
messagesarray, requiredThe conversation: objects with a role (system, developer, user, assistant, tool) and a content string (or text parts). Assistant messages may carry tool_calls; tool messages carry tool_call_id.
max_completion_tokensintegerMaximum tokens to generate (max_tokens is accepted too). Default: until the model stops or the context window is full.
temperaturenumber, 0 to 2Sampling temperature. Default 1. Lower is more deterministic.
top_pnumber, 0 to 1Nucleus sampling. Default 1.
top_kintegermini-lab extension: sample only among the k most likely tokens. With the Python SDK, pass extra_body={"top_k": 20}.
seedintegerMakes sampling reproducible for the same request.
stopstring or arrayUp to 4 sequences where generation stops.
streambooleanStream the answer as server-sent events. See Streaming.
stream_optionsobject{"include_usage": true} adds a final chunk with token usage.
toolsarrayFunction tools the model may call. The models are trained for one: calculator.
tool_choicestring"auto" (default) or "none" (the model doesn't see the tools).
nintegerOnly 1 is supported.

Parameters that would change the answer but aren't supported (n > 1, logprobs, JSON mode, penalties…) are rejected with a 400 rather than silently ignored. Unknown harmless fields such as user are ignored.

Response:

{
  "id": "chatcmpl-8f3a...",
  "object": "chat.completion",
  "created": 1790000000,
  "model": "mini-1",
  "choices": [{
    "index": 0,
    "message": {"role": "assistant", "content": "Once upon a time, there was a cat named Tom..."},
    "finish_reason": "stop"
  }],
  "usage": {"prompt_tokens": 14, "completion_tokens": 58, "total_tokens": 72}
}

finish_reason is stop (the model finished or hit a stop sequence), length (max tokens or context window reached) or tool_calls. The request id is also returned in the x-request-id header, and appears in your logs.

Streaming

With "stream": true the response is a text/event-stream: one chat.completion.chunk per data: line, as tokens are generated, then data: [DONE].

stream = client.chat.completions.create(
    model="mini-1",
    messages=[{"role": "user", "content": "Tell me a story about a dog"}],
    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:  # last chunk, with include_usage
        print("\n", chunk.usage)

If generation fails after the stream has started, a chunk with an error object is sent instead of more tokens (the SDKs raise it as an exception).

Tool calling

The models were trained to use one tool, a calculator, for arithmetic. Declare it in tools; when the model wants it, the response has finish_reason: "tool_calls" and message.tool_calls. Run the calculation yourself, append the result as a tool message, and call the API again for the final answer.

tools = [{
    "type": "function",
    "function": {
        "name": "calculator",
        "description": "Evaluate an arithmetic expression.",
        "parameters": {"type": "object", "properties": {"expression": {"type": "string"}}, "required": ["expression"]},
    },
}]
messages = [{"role": "user", "content": "What is 347 + 58?"}]
response = client.chat.completions.create(model="mini-1", messages=messages, tools=tools)
message = response.choices[0].message

if message.tool_calls:
    messages.append(message)
    for call in message.tool_calls:
        expression = json.loads(call.function.arguments)["expression"]  # "347 + 58"
        result = my_safe_calculator(expression)                         # "405" (never eval() it!)
        messages.append({"role": "tool", "tool_call_id": call.id, "content": result})
    response = client.chat.completions.create(model="mini-1", messages=messages, tools=tools)

print(response.choices[0].message.content)  # "The answer is 405."

When streaming, tool calls arrive at the end, one chunk per call with its complete arguments. The playground and chat app run this loop for you on the server.

Scratchpad reasoning

Some models think before answering. That scratchpad is returned separately from the answer, in the non-standard field message.reasoning_content (delta.reasoning_content when streaming), like other OpenAI-compatible servers do. It is billed as output tokens. The SDKs keep unknown fields, so message.reasoning_content works in Python.

Models

GET /v1/models lists the models currently served; GET /v1/models/{id} returns one. Besides OpenAI's fields, each model includes its description, context window and pricing. See Models & pricing.

curl https://mini-lab.sitesolide.fr/v1/models -H "Authorization: Bearer $MINILAB_API_KEY"

Errors

Errors use OpenAI's shape, so the SDKs raise the matching exception:

{"error": {"message": "You exceeded your current quota...", "type": "insufficient_quota", "param": null, "code": "insufficient_quota"}}
StatusCodeWhat happened
400invalid_request_errorThe request is malformed or uses an unsupported parameter (see param).
400context_length_exceededThe conversation plus max_tokens doesn't fit in the model's context window. Send fewer or shorter messages, or lower max_tokens.
401invalid_api_keyThe key is missing, wrong or revoked.
404model_not_foundNo model with this id is being served.
429insufficient_quotaYour organization has no credits left, or the key reached its spend limit. Retrying won't help: add credits.
429rate_limit_exceededToo many requests or tokens per minute for this key. Retry after the reset time.
502upstream_errorThe inference server failed. Retry.
503overloadedThe inference server is busy. Retry with backoff.
503inference_unavailableThe inference server is down or restarting. Retry shortly.

Failed requests are logged but not billed.

Rate limits

Each key is limited in requests per minute (RPM) and tokens per minute (TPM). The defaults are 60 RPM and 40,000 TPM. The playground and chat app share one budget per organization.

Every response carries the current state in OpenAI's headers:

x-ratelimit-limit-requests: 60
x-ratelimit-remaining-requests: 59
x-ratelimit-reset-requests: 1s
x-ratelimit-limit-tokens: 40000
x-ratelimit-remaining-tokens: 39744
x-ratelimit-reset-tokens: 384ms

Over the limit you get 429 rate_limit_exceeded. The SDKs retry these automatically with backoff.

Pricing and billing

Credits are prepaid. A request costs its prompt tokens times the model's input price plus its completion tokens times the output price (prices are per million tokens). It is deducted from your organization's balance as soon as it completes. When the balance reaches zero, requests get 429 insufficient_quota until you add credits.

ModelContextInput, per 1MOutput, per 1M
mini-1256$0.50$1.50

Usage from the playground and the chat app is billed the same way, and shows up in Usage and Logs with its source.