API reference

Endpoints, parameters, limits and errors

Documentation

The API follows OpenAI's, so the official SDKs and their documentation apply. The samples are in Python and assume the client from the quick start.

Pick one to fill in the samples, or see all supported models.

Authentication

Send your key as a bearer token on every request. Keys start with sk-tunai-.

Authorization: Bearer YOUR_API_KEY

Base URL: https://api.tunai.tn/v1

Chat completions

POST https://api.tunai.tn/v1/chat/completions

Supports stream, stream_options, temperature, max_tokens (up to 32000), top_p, stop, tools, tool_choice, response_format, seed, presence_penalty and frequency_penalty. They are passed on to the provider.

Streaming

stream = client.chat.completions.create(
    model="<model-id>",
    messages=[{"role": "user", "content": "Write a haiku."}],
    stream=True,
)
for chunk in stream:
    delta = chunk.choices[0].delta.content or ""
    print(delta, end="", flush=True)

Responses arrive as Server-Sent Events and end with data: [DONE], as with OpenAI.

Tool calling and JSON mode

Tool calling

response = client.chat.completions.create(
    model="<model-id>",
    messages=[{"role": "user", "content": "What's the weather in Tunis?"}],
    tools=[{
        "type": "function",
        "function": {
            "name": "get_weather",
            "parameters": {
                "type": "object",
                "properties": {"city": {"type": "string"}},
                "required": ["city"],
            },
        },
    }],
)
print(response.choices[0].message.tool_calls)

JSON mode

response = client.chat.completions.create(
    model="<model-id>",
    messages=[{"role": "user", "content": "List 3 Tunisian cities as JSON."}],
    response_format={"type": "json_object"},
)

Embeddings

POST https://api.tunai.tn/v1/embeddings

Takes a string or a list of strings, plus an optional dimensions. Use an embedding model.

response = client.embeddings.create(
    model="text-embedding-3-small",
    input=["Tunis", "Sfax", "Sousse"],
)
print(len(response.data), "vectors x", len(response.data[0].embedding), "dims")

Jev

POST https://api.tunai.tn/v1/systemone

Asks Jev, TypeSafe's model, named questions about your content and returns typed answers with their probabilities. Same key and balance; charged by input tokens only. See the Jev guide.

Models

GET https://api.tunai.tn/v1/models
GET https://api.tunai.tn/v1/models/{model}

Public, no key needed. Lists only the models we can serve right now. The same list, by provider, is on Supported models.

Token usage

Every response reports the tokens it used, which is what you're charged for.

response = client.chat.completions.create(
    model="<model-id>",
    messages=[{"role": "user", "content": "Hello!"}],
)
print(response.usage.prompt_tokens, response.usage.completion_tokens)

# When streaming, ask for a final usage chunk
stream = client.chat.completions.create(
    model="<model-id>",
    messages=[{"role": "user", "content": "Hello!"}],
    stream=True,
    stream_options={"include_usage": True},
)
for chunk in stream:
    if chunk.usage:
        print("total tokens:", chunk.usage.total_tokens)

Rate limits and request ids

Each key is limited to a per-minute request cap by default. On API keys you can set a lower limit, a monthly budget, an expiry, or limit a key to some endpoints.

Every response carries the current limit state:

x-ratelimit-limit-requests:     120
x-ratelimit-remaining-requests: 119
x-ratelimit-reset-requests:     60s
x-request-id:                   req_9e0d436bb573482f85d432f779d12304

Quote the x-request-id when you contact support. It points to the exact call and what it cost.

Errors

Errors use OpenAI's { error: { message, type, code } } shape.

400invalid_request_errorInvalid parameter. param names the field.
401invalid_request_errorMissing, invalid, revoked or expired API key.
402insufficient_quotaOut of balance, or the key reached its monthly budget.
403invalid_request_errorThis key isn't allowed to use that endpoint.
404invalid_request_errorUnknown model id or route. The message names the closest real id; the full list is on Supported models.
429rate_limit_errorRate limit exceeded for this key.
502 / 503 / 504api_errorUpstream provider error, unavailable, or timed out.
  • On a 429, wait and retry with backoff.
  • A 402 means the balance or the key's budget ran out. Top up to continue.
  • Keep keys out of client-side code and repositories.