API Documentation

Complete reference for the PrivatAI REST API. Simple, privacy-friendly, and powerful.

Introduction

The PrivatAI API provides access to LLM models (e.g. gpt-oss-120b, GLM-5.2), hosted in the EU with full GDPR compliance. All requests are transmitted over HTTPS and your prompts are not stored.

🔒 Privacy: We do not store prompts or responses. Only usage counters for billing.

Base URL

https://privatai.com/api/v1

OpenAI and Grok compatible: In your client, set only the base URL to https://privatai.com/api/v1 (e.g. OPENAI_API_BASE).

Authentication

Authenticate with your API key in the Authorization header:

http
Authorization: Bearer privat_xxxxxxxxxxxxxxxxxxxx

Find your API key in the dashboard after logging in and payment.

Chat Completion

POST /api/v1/chat/completions

Generates a response to your message. OpenAI-compatible format; by default a single JSON response, or streaming via Server-Sent Events when stream: true.

Request Body

Parameter Type Description
messages array Required. Array of messages (role, content)
model string Optional. Model key (e.g. gpt-oss-120b). If omitted: gpt-oss-120b for paid accounts. See GET /api/v1/models
stream boolean Optional, default false. If true: SSE stream
temperature, top_p, max_tokens, presence_penalty, stop, seed Optional. Standard OpenAI params. max_tokens is tier-capped (Essential 8,192, Professional 32,768). frequency_penalty and min_p are accepted but not applied — use presence_penalty instead.

Simple Request

bash
curl -X POST https://privatai.com/api/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "gpt-oss-120b", "messages": [{"role": "user", "content": "What is machine learning?"}]}'

With tools (function calling)

bash
curl -X POST https://privatai.com/api/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "messages": [
    {"role": "user", "content": "What is the weather in Berlin?"}
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Get the current weather for a city",
        "parameters": {
          "type": "object",
          "properties": {
            "location": {"type": "string"}
          },
          "required": ["location"]
        }
      }
    }
  ],
  "tool_choice": "auto",
  "stream": false
}'

Conversation (Multi-Turn)

bash
curl -X POST https://privatai.com/api/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "messages": [
    {"role": "user", "content": "What is the capital of France?"},
    {"role": "assistant", "content": "The capital of France is Paris."},
    {"role": "user", "content": "What is the population?"}
  ]
}'

Response (SSE Stream, OpenAI format)

The response is streamed as Server-Sent Events in OpenAI format:

text/event-stream
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":...,"model":"...","choices":[{"index":0,"delta":{"content":"The"},"finish_reason":null}]}
data: {"id":"chatcmpl-...","choices":[{"index":0,"delta":{"content":" capital"},"finish_reason":null}]}
data: {"id":"chatcmpl-...","choices":[{"index":0,"delta":{"content":" of"},"finish_reason":null}]}
data: {"id":"chatcmpl-...","choices":[{"index":0,"delta":{"content":" France"},"finish_reason":null}]}
data: {"id":"chatcmpl-...","choices":[{"index":0,"delta":{"content":" is"},"finish_reason":null}]}
data: {"id":"chatcmpl-...","choices":[{"index":0,"delta":{"content":" Paris"},"finish_reason":null}]}
data: {"id":"chatcmpl-...","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":15,"completion_tokens":7,"total_tokens":22}}

Thinking models (Reasoning)

Models like gpt-oss-120b and GLM-5.2 are thinking models. When streaming, they may send delta.reasoning (reasoning trace) before or interleaved with delta.content (answer).

Handling: Accumulate delta.reasoning for the reasoning (e.g. to display or debug) and delta.content for the final user-facing answer.

json
{"choices":[{"index":0,"delta":{"reasoning":"Let me analyze..."}}]}
{"choices":[{"index":0,"delta":{"content":"The answer is Paris."}}]}

Embeddings

POST /api/v1/embeddings

Turns text into vectors — for RAG (retrieval-augmented generation), semantic search, clustering and classification. OpenAI-compatible: the OpenAI SDK, LangChain and the Vercel AI SDK all work by changing only the base URL.

🔒 Stateless: we compute the vectors and return them. PrivatAI stores neither your text nor the vectors. Your documents stay with you, the index is yours. Inference runs on Scaleway in France (EU).

Models

Model Dimensions Custom dimensions? Price (EUR / 1M tokens)
qwen3-embedding-8b 4096 Yes — 32…4096 0.10 input · output free
bge-multilingual-gemma2 3584 No 0.10 input · output free

Both models are multilingual and handle German and English. Available on Essential and Professional — no premium tier needed. The default is qwen3-embedding-8b: it is Matryoshka-trained, so the vector can be shortened via dimensions without wrecking quality. That matters in practice, because pgvector's HNSW index rejects anything above 2000 dimensions — with dimensions: 1024 it fits. bge-multilingual-gemma2 cannot be shortened and answers a dimensions request with a 400 rather than silently degrading your vectors.

⚠️ Never mix models or dimensions within one index. Vectors from different models — or the same model at different dimensions — are not comparable. Changing either means re-embedding everything.

Request Body

Parameter Type Description
input string | array Required. A string, or an array of strings (batch). Pre-tokenised integer arrays are not supported.
model string Optional. Defaults to qwen3-embedding-8b.
dimensions integer Optional. Shortens the vector. qwen3-embedding-8b only (32…4096).
encoding_format string Optional. float only (the default).

Limits: up to 2,048 strings and 1 MB of text per request. Vectors are returned in the same order as the inputs.

bash
curl -X POST https://privatai.com/api/v1/embeddings \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "model": "qwen3-embedding-8b",
  "input": ["Die Kündigungsfrist beträgt drei Monate.", "Notice period is three months."],
  "dimensions": 1024
}'

Response

json
{
  "object": "list",
  "data": [
    { "object": "embedding", "index": 0, "embedding": [0.0123, -0.0456, ...] },
    { "object": "embedding", "index": 1, "embedding": [0.0211, -0.0388, ...] }
  ],
  "model": "qwen3-embedding-8b",
  "usage": { "prompt_tokens": 24, "total_tokens": 24 }
}

Embeddings have no output tokens: completion_tokens is always 0 and output is free. You are billed for input tokens only.

With the OpenAI SDK

python
from openai import OpenAI

client = OpenAI(
    api_key="YOUR_API_KEY",
    base_url="https://privatai.com/api/v1",
)

res = client.embeddings.create(
    model="qwen3-embedding-8b",
    input=["Passage eins", "Passage zwei"],
    dimensions=1024,
)

vectors = [row.embedding for row in res.data]

Check Usage

GET /api/v1/usage

Returns your current usage.

bash
curl https://privatai.com/api/v1/usage \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

json
{
  "tokens_used": 15420,
  "prompt_tokens": 5200,
  "completion_tokens": 10220,
  "requests": 89,
  "last_used": "2026-07-02T14:30:00"
}

List Models

GET /api/v1/models

Returns available models (OpenAI-compatible). GLM-5.2 only appears with a Professional plan.

bash
curl https://privatai.com/api/v1/models \
  -H "Authorization: Bearer YOUR_API_KEY"

Python Example

python
import requests
import json

API_KEY = "YOUR_API_KEY"
URL = "https://privatai.com/api/v1/chat/completions"

def chat(messages):
    response = requests.post(
        URL,
        headers={
            "Authorization": f"Bearer {API_KEY}",
            "Content-Type": "application/json"
        },
        json={"messages": messages},
        stream=True
    )

    for line in response.iter_lines():
        if line.startswith(b"data: "):
            data = json.loads(line[6:])
            if data.get("choices") and data["choices"][0].get("delta", {}).get("content"):
                print(data["choices"][0]["delta"]["content"], end="", flush=True)
            if data.get("choices") and data["choices"][0].get("finish_reason"):
                print()
                return data.get("usage")

chat([{"role": "user", "content": "Explain quantum computing in simple terms."}])

JavaScript Example

javascript
const API_KEY = 'YOUR_API_KEY';

async function chat(messages) {
  const response = await fetch('https://privatai.com/api/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ messages })
  });

  const reader = response.body.getReader();
  const decoder = new TextDecoder();

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    const chunk = decoder.decode(value);
    for (const line of chunk.split('\n')) {
      if (line.startsWith('data: ')) {
        const data = JSON.parse(line.slice(6));
        if (data.choices && data.choices[0]?.delta?.content) process.stdout.write(data.choices[0].delta.content);
        if (data.choices && data.choices[0]?.finish_reason) console.log();
      }
    }
  }
}

chat([{ role: 'user', content: 'What is the meaning of life?' }]);

Error Handling

The API returns standard HTTP status codes:

Code Meaning
200 Success
400 Bad request (e.g. missing messages)
401 Invalid or missing API key
402 Subscription inactive or budget not initialized — check/renew your plan
403 Model requires the Professional plan (e.g. GLM-5.2)
429 Rate limit or monthly allowance exceeded
500 Server error

Rate Limits

The API caps requests per minute by plan (per API key). Exceeding the limit returns HTTP 429. In addition, each plan includes a monthly usage allowance; once it is used up, the API also returns HTTP 429 until the next billing cycle. Web chat on the site is rate-limited separately.

Plan Requests / min Max tokens / response Volume / month (approx.) Models
Trial64.096Standard model
Essential308.192~ 20M tokensStandard model (gpt-oss-120b)
Professional6032.768~ 40M tokensStandard model (gpt-oss-120b)
~ 4M tokensFlagship model (GLM-5.2)
⏱️ The request limit is per API key — on HTTP 429, wait briefly and retry with backoff. The monthly volume is approximate: it depends on the model and your input/output ratio; the higher-tier flagship model consumes the allowance faster. See your live usage in the dashboard.

EU AI Act: what you need to know as an integrator

Regulation (EU) 2024/1689 applies to transparency duties since 2 August 2026. PrivatAI is the provider of a general-purpose AI system. If you build an application on it you are normally a deployer — and a provider yourself as soon as you place the result on the market under your own name. These duties are then yours, not ours:

Duty When it applies
Art. 50(1) — disclosure to users Your application interacts with natural persons. You must inform them that they are dealing with an AI system — unless that is obvious from the context.
Art. 50(4) — labelling published text You publish AI-generated text in order to inform the public on matters of public interest. Also covers deep fakes.
Art. 4 — AI literacy Always. Whoever works with the system on your side must understand it sufficiently — capabilities, limits, risks.
Art. 5 / Annex III — prohibited and high-risk uses Practices prohibited under Art. 5 are contractually excluded. High-risk use under Annex III only after your own prior conformity assessment — the service is not intended for it (GTC § 5(1)(e)).
⚠️ Machine-readable marking (Art. 50(2)): the API currently emits no marker identifying output as AI-generated — neither as a header nor in the JSON. If your own duties depend on such a marker, plan for it on your side rather than relying on the response. We assessed the question and deliberately deferred implementation pending the Art. 50(7) codes of practice and harmonised standards; the decision is documented and dated. When we do mark, it will be additive and will not break an existing integration.

Contractual basis: GTC § 5(1)(e) and Data Processing Agreement § 11. This is orientation, not legal advice — which role you occupy in a given case is a question for your own adviser.