0-sbatti AI API
Self-hosted AI inference gateway with chat, embeddings and RAG support. All models run locally via Ollama.
Authentication
Every authenticated request requires an API key in the request header. Contact the administrator to obtain a key.
curl https://ai.0-sbatti.it/v1/chat \
-H "X-API-Key: sk-your-api-key-here" \
-H "Content-Type: application/json" \
-d '{"model": "mistral:latest", "message": "Hello!"}'
Rate Limiting
Authenticated endpoints enforce a per-key rate limit. Exceeding the limit returns HTTP 429.
| Scope | Limit | Reset |
|---|---|---|
| Per API key | 100 requests | Every hour |
| Daily quota | configurable per key | Midnight UTC |
Error Codes
| Status | Meaning |
|---|---|
200 | OK — request succeeded |
201 | Created — resource was created |
400 | Bad Request — missing or invalid field |
401 | Unauthorized — missing or invalid API key |
403 | Forbidden — key is disabled or not admin |
429 | Too Many Requests — rate limit exceeded |
503 | Service Unavailable — model or DB unreachable |
Endpoints
All responses are JSON. Requests with a body must set Content-Type: application/json.
Returns the current status of the gateway, including Ollama connectivity and database health. Use this endpoint for uptime monitoring.
No authentication required. Safe to use from public monitoring tools.
{
"status": "ok",
"ollama": "up",
"database": "up",
"models_available": [
"mistral:latest",
"qwen-0sbatti:latest",
"qwen2.5-coder:3b",
"nomic-embed-text:latest"
],
"timestamp": "2026-05-24T16:00:00.000000Z"
}
curl https://ai.0-sbatti.it/health
Returns the list of AI models currently loaded in Ollama and available for inference. Pass the model value directly to /v1/chat.
{
"models": [
"mistral:latest",
"qwen-0sbatti:latest",
"qwen2.5-coder:3b",
"nomic-embed-text:latest"
]
}
curl https://ai.0-sbatti.it/v1/models
Send a message to an AI model and receive a text response. All inference runs locally — no data leaves the server. Rate limit: 100 requests/hour per key.
| Field | Type | Required | Description |
|---|---|---|---|
| message | string | required | The user message to send to the model |
| model | string | optional | Model name. Default: mistral:latest |
{
"model": "mistral:latest",
"message": "Explain the difference between REST and GraphQL"
}
{
"response": "REST is an architectural style that uses standard HTTP methods...",
"model": "mistral:latest",
"elapsed_s": 12.47
}
curl https://ai.0-sbatti.it/v1/chat \
-H "X-API-Key: sk-your-api-key" \
-H "Content-Type: application/json" \
-d '{
"model": "mistral:latest",
"message": "Explain the difference between REST and GraphQL"
}'
Converts a text string into a dense vector embedding using nomic-embed-text. Returns a 768-dimensional float array. Use these vectors for semantic search, clustering, or as input to your own RAG pipeline.
| Field | Type | Required | Description |
|---|---|---|---|
| text | string | required | The text to convert to an embedding vector |
{
"text": "Vector databases store high-dimensional embeddings"
}
{
"embedding": [0.0412, -0.1893, 0.0751, ...],
"dimensions": 768,
"elapsed_s": 0.31
}
curl https://ai.0-sbatti.it/v1/embed \
-H "X-API-Key: sk-your-api-key" \
-H "Content-Type: application/json" \
-d '{"text": "Vector databases store high-dimensional embeddings"}'
Semantic search over indexed documents combined with AI-generated answers. The pipeline: embed question → search Qdrant → build context → generate answer with Mistral.
--max-time 90 in curl or equivalent timeout in your client.| Field | Type | Required | Description |
|---|---|---|---|
| question | string | required | Natural language question to answer from documents |
| top_k | integer | optional | Number of documents to retrieve. Default: 5. Max: 20 |
{
"question": "What services are described in the documentation?",
"top_k": 5
}
{
"answer": "Based on the documents, the following services are described...",
"sources": [
{
"filename": "info_0sbatti.txt",
"relevance": 0.9012,
"excerpt": "0-sbatti.it provides cloud services including..."
}
],
"model_used": "mistral:latest",
"docs_used": 3,
"elapsed_s": 32.04,
"warning": "nessun documento con contenuto leggibile trovato"
}
The warning field is only present when no usable document content was found.
curl https://ai.0-sbatti.it/v1/rag/query \
-H "X-API-Key: sk-your-api-key" \
-H "Content-Type: application/json" \
--max-time 90 \
-d '{
"question": "What services are described in the documentation?",
"top_k": 5
}'
Creates a new API key with a configurable daily request limit. Requires the admin key (sk-admin-*). The generated key is shown only once — store it securely.
| Field | Type | Required | Description |
|---|---|---|---|
| user_id | string | optional | Identifier for the user/app owning this key. Default: "unknown" |
| name | string | optional | Human-readable label for this key. Default: auto-generated |
| daily_limit | integer | optional | Maximum requests per day. Default: 1000 |
{
"user_id": "mario.rossi",
"name": "production-app",
"daily_limit": 500
}
{
"key": "sk-a3f1b2c4d5e6f7890abc1234567890ab",
"id": 7,
"user_id": "mario.rossi",
"name": "production-app",
"daily_limit": 500
}
curl https://ai.0-sbatti.it/v1/keys/generate \
-X POST \
-H "X-API-Key: sk-admin-sbatti-2026" \
-H "Content-Type: application/json" \
-d '{
"user_id": "mario.rossi",
"name": "production-app",
"daily_limit": 500
}'
Available Models
All models run locally via Ollama. No data is sent to external services.
| Model | Type | Context | Best for |
|---|---|---|---|
| mistral:latest | chat | 32k tokens | General purpose Q&A, reasoning, Italian language |
| qwen-0sbatti:latest | chat | 32k tokens | Business management, ERP-style queries, custom fine-tuned |
| qwen2.5-coder:3b | code | 32k tokens | Code generation, completion, debugging |
| nomic-embed-text:latest | 768 dim | Semantic search, RAG pipelines, document similarity |
Code Examples
Complete examples for /v1/chat in common languages.
import requests
API_KEY = "sk-your-api-key"
BASE_URL = "https://ai.0-sbatti.it"
def chat(message: str, model: str = "mistral:latest") -> str:
response = requests.post(
f"{BASE_URL}/v1/chat",
headers={
"X-API-Key": API_KEY,
"Content-Type": "application/json",
},
json={"model": model, "message": message},
timeout=90,
)
response.raise_for_status()
return response.json()["response"]
# Example usage
answer = chat("What is machine learning?")
print(answer)
# With a different model
code = chat(
"Write a Python function to sort a list",
model="qwen2.5-coder:3b"
)
print(code)
# Embeddings example
def embed(text: str) -> list[float]:
response = requests.post(
f"{BASE_URL}/v1/embed",
headers={"X-API-Key": API_KEY, "Content-Type": "application/json"},
json={"text": text},
timeout=30,
)
response.raise_for_status()
return response.json()["embedding"]
vector = embed("Hello world")
print(f"Embedding dimensions: {len(vector)}")
const API_KEY = "sk-your-api-key";
const BASE_URL = "https://ai.0-sbatti.it";
async function chat(message, model = "mistral:latest") {
const response = await fetch(`${BASE_URL}/v1/chat`, {
method: "POST",
headers: {
"X-API-Key": API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ model, message }),
signal: AbortSignal.timeout(90_000),
});
if (!response.ok) {
const err = await response.json();
throw new Error(err.error || `HTTP ${response.status}`);
}
const data = await response.json();
return data.response;
}
// Example usage (Node.js / browser)
chat("What is the capital of Italy?")
.then(answer => console.log(answer))
.catch(console.error);
// Embeddings example
async function embed(text) {
const response = await fetch(`${BASE_URL}/v1/embed`, {
method: "POST",
headers: { "X-API-Key": API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ text }),
});
const data = await response.json();
return data.embedding; // float[]
}
embed("Hello world").then(v => console.log(`${v.length} dimensions`));
<?php
define('API_KEY', 'sk-your-api-key');
define('BASE_URL', 'https://ai.0-sbatti.it');
function aiChat(string $message, string $model = 'mistral:latest'): string
{
$ch = curl_init(BASE_URL . '/v1/chat');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_TIMEOUT => 90,
CURLOPT_HTTPHEADER => [
'X-API-Key: ' . API_KEY,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'model' => $model,
'message' => $message,
]),
]);
$body = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($code !== 200) {
$err = json_decode($body, true);
throw new \RuntimeException($err['error'] ?? "HTTP {$code}");
}
return json_decode($body, true)['response'];
}
// Example usage
try {
$answer = aiChat('Explain cloud computing in one sentence');
echo $answer . PHP_EOL;
$code = aiChat(
'Write a PHP function to validate an email',
'qwen2.5-coder:3b'
);
echo $code . PHP_EOL;
} catch (\RuntimeException $e) {
echo "Error: " . $e->getMessage() . PHP_EOL;
}
Interactive Playground
Test /v1/chat directly from your browser. Your API key is never stored.
curl https://ai.0-sbatti.it/v1/chat \
-H "X-API-Key: sk-your-api-key" \
-H "Content-Type: application/json" \
-d '{"model": "mistral:latest", "message": "What is the meaning of life?"}'