Introduction
OpenAI-compatible API gateway for AI services. Use standard OpenAI SDKs with your kai API key.
Overview
kai provides an OpenAI-compatible API for AI services. You can use standard OpenAI SDKs by simply changing the base URL to your kai instance.
Authentication
All API endpoints require Bearer token authentication. Include your API key in the Authorization header:
Authorization: Bearer YOUR_API_KEY
Rate Limiting
All endpoints are rate-limited per API token. Check the X-RateLimit-* headers in every response:
| Response Header | Description |
|---|---|
X-RateLimit-Limit |
Maximum requests per minute |
X-RateLimit-Remaining |
Requests remaining in current window |
X-RateLimit-Reset |
Unix timestamp when the window resets |
When rate limited, you'll receive a 429 response with a Retry-After header.
Concurrency
POST endpoints enforce per-token concurrency limits. The number of simultaneous POST requests allowed depends on your product tier (default: 1). If all slots are occupied, the API returns 429 with error code concurrent_request_in_progress and a Retry-After header.
GET endpoints (models, usage) are never affected by the concurrency limit.
Usage Cap
Products can have a monthly usage cap. When set, the API returns these additional response headers on POST requests:
| Response Header | Description |
|---|---|
X-Units-Remaining |
Requests remaining in current billing period |
X-Trial-Units-Remaining |
Requests remaining in trial period (only during trial) |
When the cap is reached, POST requests return 429 with usage_cap_exceeded. Only successful requests count against the cap.
Error Responses
All errors follow the OpenAI error format:
{
"error": {
"message": "Human-readable description",
"type": "error_type",
"code": "error_code",
"param": "parameter_name"
}
}
| HTTP Status | Type | Code | Description |
|---|---|---|---|
| 400 | invalid_request_error |
invalid_request |
Invalid request format or parameters |
| 400 | invalid_request_error |
text_extraction_failed |
Document text extraction failed |
| 401 | authentication_error |
invalid_api_key |
Invalid or missing API key |
| 403 | permission_error |
endpoint_not_allowed |
Token lacks required endpoint permission |
| 403 | permission_error |
subscription_inactive |
Subscription is not active |
| 403 | permission_error |
token_inactive |
Token has been deactivated |
| 403 | permission_error |
token_expired |
Token has expired |
| 429 | rate_limit_error |
rate_limit_exceeded |
Rate limit exceeded |
| 429 | rate_limit_error |
concurrent_request_in_progress |
Another request is still processing |
| 429 | rate_limit_error |
usage_cap_exceeded |
Monthly usage cap or trial limit exceeded |
| 500 | server_error |
internal_error |
Internal server error |
| 503 | server_error |
service_unavailable |
Service temporarily unavailable |
Authenticating requests
To authenticate requests, include an Authorization header with the value "Bearer {YOUR_API_KEY}".
All authenticated endpoints are marked with a requires authentication badge in the documentation below.
Contact office@kaino.io to obtain an API key.
Chat
Create a chat completion.
requires authentication
Generates a model response for the given conversation. Follows the OpenAI chat completions format. Token usage is included in successful responses and counted toward your billing period.
Streaming is not currently supported — set stream to false or omit it.
Example request:
curl --request POST \
"https://mconnect.kai.kaino.io/api/v1/chat/completions" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"messages\": [
{
\"role\": \"system\",
\"content\": \"You are a helpful assistant.\"
},
{
\"role\": \"user\",
\"content\": \"Hello!\"
}
],
\"model\": \"\",
\"temperature\": 0.7,
\"max_tokens\": 1024,
\"top_p\": 1,
\"stream\": false,
\"n\": 1,
\"presence_penalty\": 0,
\"frequency_penalty\": 0
}"
const url = new URL(
"https://mconnect.kai.kaino.io/api/v1/chat/completions"
);
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"messages": [
{
"role": "system",
"content": "You are a helpful assistant."
},
{
"role": "user",
"content": "Hello!"
}
],
"model": "",
"temperature": 0.7,
"max_tokens": 1024,
"top_p": 1,
"stream": false,
"n": 1,
"presence_penalty": 0,
"frequency_penalty": 0
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());import requests
import json
url = 'https://mconnect.kai.kaino.io/api/v1/chat/completions'
payload = {
"messages": [
{
"role": "system",
"content": "You are a helpful assistant."
},
{
"role": "user",
"content": "Hello!"
}
],
"model": "",
"temperature": 0.7,
"max_tokens": 1024,
"top_p": 1,
"stream": false,
"n": 1,
"presence_penalty": 0,
"frequency_penalty": 0
}
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()$client = new \GuzzleHttp\Client();
$url = 'https://mconnect.kai.kaino.io/api/v1/chat/completions';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'messages' => [
[
'role' => 'system',
'content' => 'You are a helpful assistant.',
],
[
'role' => 'user',
'content' => 'Hello!',
],
],
'model' => '',
'temperature' => 0.7,
'max_tokens' => 1024,
'top_p' => 1.0,
'stream' => false,
'n' => 1,
'presence_penalty' => 0.0,
'frequency_penalty' => 0.0,
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));Example response (200, Successful completion):
{
"id": "chatcmpl-kai-abc123",
"object": "chat.completion",
"model": "Meta-Llama-3_3-70B-Instruct",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! How can I help you today?"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 25,
"completion_tokens": 12,
"total_tokens": 37
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Audio
Transcribe audio or video.
requires authentication
Transcribes speech from an audio or video file. For video uploads, the audio track is automatically extracted before transcription. The file must contain at least one audio stream.
Compatible with the OpenAI Whisper API format.
Example request:
curl --request POST \
"https://mconnect.kai.kaino.io/api/v1/audio/transcriptions" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: multipart/form-data" \
--header "Accept: application/json" \
--form "model="\
--form "language=de"\
--form "prompt=This is a medical consultation."\
--form "response_format=json"\
--form "temperature=0"\
--form "file=@/tmp/php5bfpljt5o55k46SM913" const url = new URL(
"https://mconnect.kai.kaino.io/api/v1/audio/transcriptions"
);
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "multipart/form-data",
"Accept": "application/json",
};
const body = new FormData();
body.append('model', '');
body.append('language', 'de');
body.append('prompt', 'This is a medical consultation.');
body.append('response_format', 'json');
body.append('temperature', '0');
body.append('file', document.querySelector('input[name="file"]').files[0]);
fetch(url, {
method: "POST",
headers,
body,
}).then(response => response.json());import requests
import json
url = 'https://mconnect.kai.kaino.io/api/v1/audio/transcriptions'
files = {
'model': (None, ''),
'language': (None, 'de'),
'prompt': (None, 'This is a medical consultation.'),
'response_format': (None, 'json'),
'temperature': (None, '0'),
'file': open('/tmp/php5bfpljt5o55k46SM913', 'rb')}
payload = {
"model": "",
"language": "de",
"prompt": "This is a medical consultation.",
"response_format": "json",
"temperature": 0
}
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'multipart/form-data',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, files=files)
response.json()$client = new \GuzzleHttp\Client();
$url = 'https://mconnect.kai.kaino.io/api/v1/audio/transcriptions';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'multipart/form-data',
'Accept' => 'application/json',
],
'multipart' => [
[
'name' => 'model',
'contents' => ''
],
[
'name' => 'language',
'contents' => 'de'
],
[
'name' => 'prompt',
'contents' => 'This is a medical consultation.'
],
[
'name' => 'response_format',
'contents' => 'json'
],
[
'name' => 'temperature',
'contents' => '0'
],
[
'name' => 'file',
'contents' => fopen('/tmp/php5bfpljt5o55k46SM913', 'r')
],
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));Example response (200, Successful transcription):
{
"text": "Der Patient berichtet über anhaltende Kopfschmerzen seit zwei Wochen."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Documents
Anonymize documents by detecting and replacing PII with placeholders.
requires authentication
Upload 1 file (PDF, DOCX, images, plain text). The API extracts text
(with OCR fallback for scanned documents), detects personally identifiable
information, and replaces it with numbered placeholders like
[[NAME_1]], [[EMAIL_2]].
Returns the sanitized text, a sanitized filename, and a mapping of placeholders to their original values.
Requires the documents_anonymize permission on your API token's product.
Example request:
curl --request POST \
"https://mconnect.kai.kaino.io/api/v1/documents/anonymize" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: multipart/form-data" \
--header "Accept: application/json" \
--form "pii_types[]=name"\
--form "files[]=@/tmp/phpek41o6mlfcls1NrfqBy" const url = new URL(
"https://mconnect.kai.kaino.io/api/v1/documents/anonymize"
);
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "multipart/form-data",
"Accept": "application/json",
};
const body = new FormData();
body.append('pii_types[]', 'name');
body.append('files[]', document.querySelector('input[name="files[]"]').files[0]);
fetch(url, {
method: "POST",
headers,
body,
}).then(response => response.json());import requests
import json
url = 'https://mconnect.kai.kaino.io/api/v1/documents/anonymize'
files = {
'pii_types[]': (None, 'name'),
'files[]': open('/tmp/phpek41o6mlfcls1NrfqBy', 'rb')}
payload = {
"pii_types": [
"name"
]
}
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'multipart/form-data',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, files=files)
response.json()$client = new \GuzzleHttp\Client();
$url = 'https://mconnect.kai.kaino.io/api/v1/documents/anonymize';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'multipart/form-data',
'Accept' => 'application/json',
],
'multipart' => [
[
'name' => 'pii_types[]',
'contents' => 'name'
],
[
'name' => 'files[]',
'contents' => fopen('/tmp/phpek41o6mlfcls1NrfqBy', 'r')
],
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));Example response (200, Successful anonymization):
{
"object": "list",
"data": [
{
"original_filename": "patient_report.pdf",
"sanitized_filename": "patient_report.pdf",
"sanitized_text": "Patient: [[NAME_1]]\nDiagnose: [[MEDICAL_1]]",
"placeholders": {
"[[NAME_1]]": "Max Mustermann",
"[[MEDICAL_1]]": "Diabetes Typ 2"
},
"metadata": {
"extraction_method": "pdf_parser",
"used_ocr": false,
"page_count": 2,
"strategy": "ai"
}
}
]
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Strip metadata from a document and return the cleaned file.
requires authentication
Upload a single file (PDF, image, DOCX, XLSX, XLS, or text). Metadata (EXIF, author, company, timestamps, etc.) is removed and the cleaned file is returned as a binary download.
Requires the documents_strip_metadata permission on your API token's product.
Example request:
curl --request POST \
"https://mconnect.kai.kaino.io/api/v1/documents/strip-metadata" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "X-Metadata-Stripped: true" \
--header "X-Strip-Method: imagemagick" \
--header "X-Stripped-Fields: EXIF,IPTC,XMP,ICC" \
--header "X-Original-Size: 245760" \
--header "X-Sanitized-Size: 101500" \
--header "Content-Type: multipart/form-data" \
--header "Accept: application/json" \
--form "file=@/tmp/php0va7si6c9gak5RfjCmk" const url = new URL(
"https://mconnect.kai.kaino.io/api/v1/documents/strip-metadata"
);
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"X-Metadata-Stripped": "true",
"X-Strip-Method": "imagemagick",
"X-Stripped-Fields": "EXIF,IPTC,XMP,ICC",
"X-Original-Size": "245760",
"X-Sanitized-Size": "101500",
"Content-Type": "multipart/form-data",
"Accept": "application/json",
};
const body = new FormData();
body.append('file', document.querySelector('input[name="file"]').files[0]);
fetch(url, {
method: "POST",
headers,
body,
}).then(response => response.json());import requests
import json
url = 'https://mconnect.kai.kaino.io/api/v1/documents/strip-metadata'
files = {
'file': open('/tmp/php0va7si6c9gak5RfjCmk', 'rb')}
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'X-Metadata-Stripped': 'true',
'X-Strip-Method': 'imagemagick',
'X-Stripped-Fields': 'EXIF,IPTC,XMP,ICC',
'X-Original-Size': '245760',
'X-Sanitized-Size': '101500',
'Content-Type': 'multipart/form-data',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, files=files)
response.json()$client = new \GuzzleHttp\Client();
$url = 'https://mconnect.kai.kaino.io/api/v1/documents/strip-metadata';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'X-Metadata-Stripped' => 'true',
'X-Strip-Method' => 'imagemagick',
'X-Stripped-Fields' => 'EXIF,IPTC,XMP,ICC',
'X-Original-Size' => '245760',
'X-Sanitized-Size' => '101500',
'Content-Type' => 'multipart/form-data',
'Accept' => 'application/json',
],
'multipart' => [
[
'name' => 'file',
'contents' => fopen('/tmp/php0va7si6c9gak5RfjCmk', 'r')
],
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));Example response (200, Cleaned file as binary download):
<<Binary file content>>
Example response (400, Metadata stripping failed):
{
"error": {
"message": "Failed to strip metadata from 'photo.jpg'",
"type": "invalid_request_error",
"param": "file",
"code": "metadata_stripping_failed"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Models
List available models.
requires authentication
Returns models available for your API token. Use the model id values
when calling chat or audio endpoints.
Example request:
curl --request GET \
--get "https://mconnect.kai.kaino.io/api/v1/models" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://mconnect.kai.kaino.io/api/v1/models"
);
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());import requests
import json
url = 'https://mconnect.kai.kaino.io/api/v1/models'
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()$client = new \GuzzleHttp\Client();
$url = 'https://mconnect.kai.kaino.io/api/v1/models';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));Example response (200, Available models):
{
"object": "list",
"data": [
{
"id": "Meta-Llama-3_3-70B-Instruct",
"object": "model",
"created": 1700000000,
"owned_by": "kai"
},
{
"id": "mistral-nemo-instruct-2407",
"object": "model",
"created": 1700000000,
"owned_by": "kai"
},
{
"id": "whisper-large-v3-turbo",
"object": "model",
"created": 1700000000,
"owned_by": "kai"
}
]
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Usage
Get current billing period usage statistics.
requires authentication
Returns token status, product info, and usage counts for the current month.
The status field indicates whether the token is currently usable (active)
or not (inactive).
Example request:
curl --request GET \
--get "https://mconnect.kai.kaino.io/api/v1/usage" \
--header "Authorization: Bearer {YOUR_API_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://mconnect.kai.kaino.io/api/v1/usage"
);
const headers = {
"Authorization": "Bearer {YOUR_API_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());import requests
import json
url = 'https://mconnect.kai.kaino.io/api/v1/usage'
headers = {
'Authorization': 'Bearer {YOUR_API_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()$client = new \GuzzleHttp\Client();
$url = 'https://mconnect.kai.kaino.io/api/v1/usage';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));Example response (200, Usage statistics):
{
"status": "active",
"is_free": false,
"is_trial": false,
"trial_ends_at": null,
"period": "2025-01",
"product": "aegis-pro",
"product_label": "Aegis PRO",
"usage_label": "API Requests",
"total_requests": 42,
"total_ok": 40,
"total_errors": 2,
"total_tokens_in": 12500,
"total_tokens_out": 3200,
"rate_limit_per_minute": 10,
"max_concurrent_requests": 1,
"monthly_usage_cap": 100,
"usage_remaining": 60,
"trial_usage_limit": null,
"trial_usage_remaining": null,
"max_file_size": 26214400,
"addons": [],
"enabled_features": [],
"enabled_endpoints": [
"chat_completions",
"audio_transcriptions",
"documents_anonymize"
]
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Response
Response Fields
is_free
boolean
Whether this is an anonymous free product key (no user account associated).
rate_limit_per_minute
integer
Maximum requests per minute for this token.
max_concurrent_requests
integer
Maximum simultaneous POST requests allowed.
monthly_usage_cap
integer
Maximum successful requests per month. Null if unlimited.
usage_remaining
integer
Remaining requests in current month. Null if unlimited.
trial_usage_limit
integer
Total requests allowed during trial. Null if not on trial or no trial limit.
trial_usage_remaining
integer
Remaining trial requests. Null if not on trial or no trial limit.