siati.ai docs

API reference

Audio — Speech-to-Text & Text-to-Speech

POST /v1/audio/transcriptions e /v1/audio/speech — OpenAI-compatible. Whisper large-v3 self-hosted in Svizzera + Piper TTS multilingua.

Last updated: 2026-05-26

Audio API — Speech-to-Text & Text-to-Speech

Due endpoint, due use case:

Endpoint Funzione Modello Backend
POST /v1/audio/transcriptions Audio → testo (STT) Whisper large-v3 / large-v3-turbo GPU on-prem, Svizzera
POST /v1/audio/speech Testo → audio (TTS) Piper multilingua CPU on-prem, Svizzera

Sovranità: tutti gli audio e i testi restano sull'infrastruttura siati.ai. Nessun dato sale a cloud terzi (OpenAI, Google, Microsoft).


Speech-to-Text

POST https://api.siati.ai/v1/audio/transcriptions

Trascrive un file audio in testo. Auto-detect lingua di default, o forzala con language. Supporta italiano, inglese, tedesco, francese e altre 90+ lingue.

Request — cURL

bash
curl https://api.siati.ai/v1/audio/transcriptions \
  -H "Authorization: Bearer $SIATI_API_KEY" \
  -F file=@messaggio.m4a \
  -F model=whisper-1 \
  -F language=it

Request — Python (OpenAI SDK)

python
from openai import OpenAI

client = OpenAI(
    base_url="https://api.siati.ai/v1",
    api_key="sk-siati-...",
)

with open("messaggio.m4a", "rb") as f:
    result = client.audio.transcriptions.create(
        model="whisper-1",
        file=f,
        language="it",                  # opzionale, auto-detect altrimenti
        response_format="json",         # json | verbose_json | text
        prompt="vocabolario tecnico",   # hint per migliorare trascrizione
    )

print(result.text)

Request — JavaScript / Node (fetch)

javascript
const form = new FormData();
form.append('file', audioBlob, 'messaggio.m4a');
form.append('model', 'whisper-1');
form.append('language', 'it');

const res = await fetch('https://api.siati.ai/v1/audio/transcriptions', {
  method: 'POST',
  headers: { 'Authorization': `Bearer ${SIATI_API_KEY}` },
  body: form,
});
const { text } = await res.json();
console.log(text);

Request — Swift (iOS, URLSession)

swift
let url = URL(string: "https://api.siati.ai/v1/audio/transcriptions")!
let boundary = UUID().uuidString
var req = URLRequest(url: url)
req.httpMethod = "POST"
req.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
req.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")

var body = Data()
// audio file part
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"file\"; filename=\"audio.m4a\"\r\n".data(using: .utf8)!)
body.append("Content-Type: audio/m4a\r\n\r\n".data(using: .utf8)!)
body.append(audioData)
body.append("\r\n".data(using: .utf8)!)
// model + language fields
for (k, v) in [("model", "whisper-1"), ("language", "it")] {
    body.append("--\(boundary)\r\n".data(using: .utf8)!)
    body.append("Content-Disposition: form-data; name=\"\(k)\"\r\n\r\n\(v)\r\n".data(using: .utf8)!)
}
body.append("--\(boundary)--\r\n".data(using: .utf8)!)
req.httpBody = body

let (data, _) = try await URLSession.shared.data(for: req)
let result = try JSONDecoder().decode(TranscriptionResponse.self, from: data)
print(result.text)

Parameters

Param Type Required Description
file binary Audio file. Formati: wav, mp3, m4a, mp4, ogg, webm, flac. Max 25 MB.
model string whisper-1 (default, ~10× faster) o whisper-1-hd (max accuracy).
language string ISO-639-1 (es. it, en, de, fr). Auto-detect se omesso.
prompt string Hint contestuale (max 1024 char). Migliora la trascrizione su vocabolario tecnico o nomi propri.
response_format string json (default), verbose_json (con segments + timestamps), o text (solo plain text).
temperature float 0..1, default 0. Aumentare se la trascrizione "sbatte" su parole rare.

Response — response_format=json (default)

json
{
  "text": "Ciao, come stai oggi? Volevo chiederti se possiamo vederci domani."
}

Response — response_format=verbose_json

json
{
  "task": "transcribe",
  "language": "it",
  "duration": 4.32,
  "text": "Ciao, come stai oggi? Volevo chiederti se possiamo vederci domani.",
  "segments": [
    {
      "id": 0,
      "start": 0.0,
      "end": 2.1,
      "text": "Ciao, come stai oggi?",
      "avg_logprob": -0.21,
      "no_speech_prob": 0.02
    },
    {
      "id": 1,
      "start": 2.1,
      "end": 4.32,
      "text": "Volevo chiederti se possiamo vederci domani."
    }
  ]
}

Quale modello scegliere?

Use case Modello consigliato
Voice messages WhatsApp-style (5–60s) whisper-1 (turbo) — più veloce, qualità eccellente su clip brevi
Dictation lunga (1–10 min) whisper-1 per default; passa a whisper-1-hd se utente lamenta errori
Audio rumoroso, accenti regionali pesanti, audio telefonico whisper-1-hd (large-v3 puro)
Trascrizione meeting/podcast whisper-1-hd + response_format=verbose_json

Text-to-Speech

POST https://api.siati.ai/v1/audio/speech

Sintetizza voce a partire da testo. Sei voci installate in quattro lingue.

Request — cURL

bash
curl https://api.siati.ai/v1/audio/speech \
  -H "Authorization: Bearer $SIATI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "tts-1",
    "voice": "paola",
    "input": "Ciao! Sono Paola, la voce italiana di SIATI.",
    "response_format": "wav"
  }' \
  --output benvenuto.wav

Request — Python (OpenAI SDK)

python
res = client.audio.speech.create(
    model="tts-1",                        # accettato per compat OpenAI
    voice="paola",                        # paola, riccardo, amy, ryan, thorsten, siwis
    input="Ciao! Sono Paola.",
    response_format="wav",                # wav | mp3
    # speed=1.0,                          # accettato ma ignorato dal MVP
)

res.stream_to_file("benvenuto.wav")

Request — JavaScript / Node

javascript
const res = await fetch('https://api.siati.ai/v1/audio/speech', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${SIATI_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: 'tts-1',
    voice: 'paola',
    input: 'Ciao!',
    response_format: 'mp3',
  }),
});
const audioBlob = await res.blob();
// In browser:
const url = URL.createObjectURL(audioBlob);
new Audio(url).play();

Parameters

Param Type Required Description
input string Testo da sintetizzare. Max 5000 caratteri per request.
voice string Nome voce (vedi tabella sotto). Case-sensitive.
model string Accettato per compat (tts-1, tts-1-hd). Piper è single-model.
response_format string wav (default) o mp3. WAV = qualità lossless, MP3 = 80% più piccolo.
speed float 0.5..2.0. Accettato per compat OpenAI ma ignorato nel MVP (Piper non supporta speed control nativamente).
language string Estensione siati (non OpenAI). ISO-639-1 per scegliere voce default se voice omesso.

Voci disponibili

Voce Lingua Genere Caratteristiche
paola 🇮🇹 Italiano Femminile Default IT. Prosodia naturale, ottima per assistant vocale.
riccardo 🇮🇹 Italiano Maschile Voce leggera/mobile-friendly. Bitrate ridotto.
amy 🇺🇸 Inglese Femminile Default EN. Prosodia US standard, news-anchor style.
ryan 🇺🇸 Inglese Maschile Voce maschile US, leggermente più calda.
thorsten 🇩🇪 Tedesco Maschile Default DE. Voce solida, leggermente formale.
siwis 🇫🇷 Francese Femminile Default FR. Prosodia parigina chiara.

Response

Il body è il file audio binario. Headers di rilievo:

http
Content-Type: audio/wav     (o audio/mpeg per mp3)
Content-Length: 152043
X-Request-Id: req_abc123...

Mobile API (my.siati.ai/api/v1/audio/*)

Per l'app mobile (iOS/Android/web chat) gli endpoint sono autenticati con JWT invece che API key, e usano un naming siati-native (snake_case esplicito) anziché lo schema OpenAI:

Endpoint mobile Equivalente dev
POST /api/v1/audio/transcribe (form field audio) POST /v1/audio/transcriptions (form field file)
POST /api/v1/audio/synthesize (JSON text) POST /v1/audio/speech (JSON input)
GET /api/v1/audio/voices – (nuova, ritorna catalogo voci)

Esempio mobile transcribe

bash
curl https://my.siati.ai/api/v1/audio/transcribe \
  -H "Authorization: Bearer $JWT_TOKEN" \
  -F audio=@nota.m4a \
  -F language=it \
  -F with_segments=true

Response:

json
{
  "text": "Promemoria: chiamare Marco alle 15.",
  "language": "it",
  "duration_seconds": 3.4,
  "model": "whisper-1",
  "segments": [...]
}

Esempio mobile synthesize

bash
curl https://my.siati.ai/api/v1/audio/synthesize \
  -H "Authorization: Bearer $JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"text":"Ciao!","voice":"paola","format":"mp3"}' \
  --output out.mp3

Esempio mobile lista voci

bash
curl https://my.siati.ai/api/v1/audio/voices \
  -H "Authorization: Bearer $JWT_TOKEN"
json
{
  "voices": [
    { "name": "paola", "language": "it", "gender": "female" },
    { "name": "riccardo", "language": "it", "gender": "male" },
    ...
  ],
  "defaults_by_language": {
    "it": "paola",
    "en": "amy",
    "de": "thorsten",
    "fr": "siwis",
    "rm": "paola"
  }
}

Errors

Schema standard. Vedi Errors per il dettaglio.

Status Code Quando
400 invalid_request_error File mancante, modello sconosciuto, JSON malformato.
401 invalid_api_key API key non valida o revocata. Vedi Authentication.
413 request_too_large Audio sopra 25 MB o input TTS sopra 5000 char.
415 unsupported_media_type Formato audio non supportato. Convertilo in wav/mp3/m4a.
429 rate_limit_exceeded Vedi Rate limits. Include Retry-After.
502 bad_gateway Backend STT/TTS momentaneamente irraggiungibile. Retry esponenziale.

Rate limits

Endpoint Limite Periodo
POST /v1/audio/transcriptions 30 req 1 min
POST /v1/audio/speech 120 req 1 min
POST /api/v1/audio/transcribe (mobile) 30 req 1 min
POST /api/v1/audio/synthesize (mobile) 60 req 1 min

I limiti sono pensati per uso interattivo umano + batch jobs ragionevoli. Per quote più alte contatta info@siati.ai.


Pricing

Operazione Costo
STT (qualsiasi modello) 0.006 CHF / minuto di audio
TTS 5 CHF / 1M caratteri (≈ gratis nel pratico)

Esempi:

  • 30s di voice message trascritto: 0.003 CHF
  • 1h di meeting trascritto: 0.36 CHF
  • 1000 frasi TTS da 100 char ciascuna: 0.50 CHF

Lo storico consumo è visibile nella dashboard.


Tips & pattern comuni

Voice mode chat (utente parla → AI risponde a voce)

python
# 1. Trascrivi voice message utente
with open("user-message.m4a", "rb") as f:
    transcript = client.audio.transcriptions.create(model="whisper-1", file=f, language="it")

# 2. Chiedi al LLM
resp = client.chat.completions.create(
    model="apertus-70b-instruct",
    messages=[{"role": "user", "content": transcript.text}],
)
ai_text = resp.choices[0].message.content

# 3. Sintetizza la risposta
audio = client.audio.speech.create(model="tts-1", voice="paola", input=ai_text)
audio.stream_to_file("ai-reply.mp3")

Dictation con vocabolario di dominio

Quando trascrivi audio con termini tecnici o nomi propri ricorrenti, passa prompt:

python
client.audio.transcriptions.create(
    model="whisper-1-hd",
    file=open("riunione.m4a", "rb"),
    language="it",
    prompt="SIATI, Apertus, Mistral, Qdrant, GPU, embedding, RAG, tier",
    response_format="verbose_json",
)

Whisper userà il prompt per "biasare" verso quella terminologia → meno errori su nomi propri.

Streaming TTS per voice mode realtime


SDK & risorse

  • OpenAI Python SDK: pip install openai → cambia base_url. Docs.
  • OpenAI Node SDK: npm i openai → cambia baseURL. Docs.
  • Postman collection: scrivi a info@siati.ai per richiederla.
  • Issue & feedback: support@siati.ai.