Speech synthesis, transcription, and audio analysis via a single REST API. Key in the header; result as a binary audio file or JSON.
Open Swagger (OpenAPI)rtt_… in your dashboard.X-Api-Key header of every request.https://ttsapi.ru.curl -X POST https://ttsapi.ru/v1/synthesize \
-H "Content-Type: application/json" \
-H "X-Api-Key: rtt_…" \
-d '{"text":"Привет, мир!","voice":"preset_anna","format":"mp3"}' \
--output hello.mp3
All endpoints /v1/* (except the voice catalog) require an API key in the X-Api-Key.
X-Api-Key: rtt_…
Also supported: Authorization: Bearer rtt_… and X-RapidAPI-Key for RapidAPI integration.
Keys are created and managed in your dashboard (`/account`). A key is shown only once – copy it right away. Each key can have a name, an expiry date, access scopes, and its own monthly limits.
A scope restricts which endpoints a key may call. By default a key is created without restrictions (all scopes); plan limits still apply separately.
| Scope | Description |
|---|---|
synthesis | /v1/synthesize, /v1/synthesize/stream, /v1/batch/synthesize |
transcription | /v1/transcribe, /v1/transcribe/sync, /v1/transcribe/{jobId}, /v1/transcribe/{jobId}/subtitles, /v1/transcribe/{jobId}/translate, /v1/audio/clean, /v1/recordings, /v1/recordings/from-link, /v1/recordings/{id}/* (speakers, transcript, export, share), /v1/eval, /v1/qa/*, /v1/meetings/protocol, /v1/search, /v1/ask |
analysis | /v1/analyze, /v1/analyze/sync, /v1/analyze/{jobId}, /v1/detect-language, /v1/redact, /v1/analyze/topics, /v1/analyze/summarize, /v1/moderate, /v1/batch/analyze |
voices | /v1/voices/clone, /v1/voices/clone/{id} |
voiceid | /v1/voice-id, /v1/voice-id/enroll, /v1/voice-id/verify, /v1/voice-id/identify, /v1/voice-id/profiles |
effects | /v1/audio/effects, /v1/video/effects (включая опрос и скачивание результата) |
An empty scope set means «unrestricted» – the behaviour of keys created before scopes existed. If a key lacks a required scope, the API returns `403`.
A key can have an expiry date and three monthly limits on top of the plan quota: synthesis characters, transcription seconds, and analysis requests. Expired or revoked keys stop authenticating (`401`). Exceeding a per-key limit returns `403 quota_exceeded`.
Synthesis characters, transcription seconds, and analysis requests are tracked per key and shown in the dashboard and admin panel – handy for giving separate integrations their own quotas.
Create, rename, and revoke keys in your dashboard (`/account/keys`). The number of active keys is limited by plan (Free – 2, Basic – 5, Pro – 10, Business – 20). Admins manage client keys in the admin panel.
POST /v1/synthesize accepts JSON and returns a binary audio file (mp3, wav, or ogg).
| Field | Type | Description |
|---|---|---|
text | string | Text to synthesize (up to 5000 characters) |
voice | string | Voice ID, e.g. preset_anna |
format | string | mp3 (default), wav, ogg |
sample_rate | int | Sample rate, e.g. 24000 |
speed | float | Speech speed (default 1.0) |
language | string | Synthesis language (defaults to the voice language): ru, en, zh, ja, ko, de, es, fr, it Defaults to the voice language when omitted. |
put_accent | bool | Accepted for backward compatibility; does not affect synthesis |
put_yo | bool | Restore the letter ё: accepted for backward compatibility and ignored |
normalize | bool | Spell numbers out in words: accepted for backward compatibility and ignored — the engine already reads digits correctly |
lexicon | object | Pronunciation dictionary: key – as written in the text, value – how to say it. Whole-word, case-insensitive substitution, up to 200 entries. Example: {"ООО": "общество с ограниченной ответственностью"} |
effects | string | Optional audio-effects chain – a JSON string holding an array of effect descriptors. Requires a Pro or Business plan. |
The lexicon changes only what the model hears: quota is counted on the original text. Ignored for SSML requests (ssml=true).
The put_accent, put_yo and normalize parameters are accepted for backward compatibility. The engine does not consume stress marks, already reads digits correctly and does not let you force the letter ё, so none of them affect the audio. Use lexicon to fix the pronunciation of individual words.
Effects are applied to the synthesized audio and encoded into the requested format. Not supported for streaming synthesis (/v1/synthesize/stream).
curl -X POST https://ttsapi.ru/v1/synthesize \
-H "Content-Type: application/json" \
-H "X-Api-Key: rtt_…" \
-d '{"text":"Добрый день!","voice":"preset_anna","format":"mp3"}' \
--output speech.mp3
# Audio effects (Pro/Business): reverb + pitch
curl -X POST https://ttsapi.ru/v1/synthesize \
-H "Content-Type: application/json" \
-H "X-Api-Key: rtt_…" \
-d '{"text":"Добрый день!","voice":"preset_anna","format":"mp3","effects":"[{\"type\":\"reverb\",\"room_size\":0.5},{\"type\":\"pitch\",\"semitones\":2}]"}' \
--output speech_fx.mp3
import httpx
response = httpx.post(
"https://ttsapi.ru/v1/synthesize",
headers={"X-Api-Key": "rtt_…"},
json={
"text": "Добрый день!",
"voice": "preset_anna",
"format": "mp3",
},
timeout=60.0,
)
response.raise_for_status()
with open("speech.mp3", "wb") as f:
f.write(response.content)
const response = await fetch("https://ttsapi.ru/v1/synthesize", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": "rtt_…",
},
body: JSON.stringify({ text: "Добрый день!", voice: "preset_anna", format: "mp3" }),
});
if (!response.ok) throw new Error(await response.text());
const blob = await response.blob();
// Browser: URL.createObjectURL(blob) → play it in an audio tag
// Node.js: fs.writeFileSync("speech.mp3", Buffer.from(await blob.arrayBuffer()))
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "https://ttsapi.ru/v1/synthesize");
request.Headers.Add("X-Api-Key", "rtt_…");
request.Content = new StringContent(
"""{"text":"Добрый день!","voice":"preset_anna","format":"mp3"}""",
System.Text.Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
await File.WriteAllBytesAsync("speech.mp3", await response.Content.ReadAsByteArrayAsync());
POST /v1/synthesize/stream returns audio chunks as they are generated – playback starts before the whole text is synthesized. Available on the Pro and Business; Free and Basic receive 403 streaming_forbidden.
The request body is the same as POST /v1/synthesize. The response is an audio/mpeg stream without a Content-Length: each chunk is written as soon as it is synthesized.
curl -X POST https://ttsapi.ru/v1/synthesize/stream \
-H "Content-Type: application/json" \
-H "X-Api-Key: rtt_…" \
-d '{"text":"Первое предложение. Второе предложение. Третье предложение.","voice":"preset_anna","format":"mp3"}' \
--no-buffer \
--output speech_stream.mp3
import httpx
with httpx.stream(
"POST",
"https://ttsapi.ru/v1/synthesize/stream",
headers={"X-Api-Key": "rtt_…"},
json={"text": "Первое предложение. Второе предложение.", "voice": "preset_anna", "format": "mp3"},
timeout=60.0,
) as response:
response.raise_for_status()
with open("speech_stream.mp3", "wb") as f:
for chunk in response.iter_bytes():
f.write(chunk)
MP3 streams sentence by sentence (MP3 frames concatenate cleanly); WAV/OGG are encoded whole and sliced into chunks so the stream stays a valid container.
POST /v1/synthesize/async queues long text (audiobook / long article) as a background job. The text is split into paragraphs, each synthesized separately, then concatenated into a single sample-accurate WAV with a timed manifest.
Poll `GET /v1/synthesize/async/{job_id}` for the manifest and download the audio from `GET /v1/synthesize/async/{job_id}/audio`. Pass `webhookUrl` to be notified on completion.
Long-form synthesis returns WAV (PCM) so paragraph timestamps stay sample-accurate.
| Field | Type | Description |
|---|---|---|
text | string | Text to synthesize (up to 5000 characters) |
voice | string | Voice ID, e.g. preset_anna |
format | string | wav (default), |
sample_rate | int | Sample rate, e.g. 24000 |
speed | float | Speech speed (default 1.0) |
language | string | Synthesis language (defaults to the voice language): |
curl -X POST "https://ttsapi.ru/v1/synthesize/async" \
-H "Content-Type: application/json" \
-H "X-Api-Key: rtt_…" \
-d '{"text":"Глава первая. Абзац один.\n\nАбзац два.","voice":"preset_anna","format":"wav"}'
# → 202 { "job_id": "…", "status": "queued", "estimated_seconds": 12 }
# Result
curl "https://ttsapi.ru/v1/synthesize/async/{job_id}" \
-H "X-Api-Key: rtt_…"
# → 200 { "job_id": "…", "status": "completed", "result": { "segments": [ … ], "duration_milliseconds": 18420, … } }
curl "https://ttsapi.ru/v1/synthesize/async/{job_id}/audio" \
-H "X-Api-Key: rtt_…" \
--output audiobook.wav
from voicekit import VoiceKitClient
client = VoiceKitClient(api_key="rtt_…")
job = client.synthesize_async("Глава первая…", voice="preset_anna", format="wav")
result = client.get_synthesis_job(job["job_id"])
while result["status"] not in ("completed", "failed"):
result = client.get_synthesis_job(job["job_id"])
audio = client.download_synthesis_audio(job["job_id"])
open("audiobook.wav", "wb").write(audio)
POST /v1/audio/effects applies a chain of audio effects to an uploaded file as a background job. Supported: reverb, compressor, eq, distortion, chorus, pitch, timestretch.
Poll `GET /v1/audio/effects/{job_id}` and download the result from `GET /v1/audio/effects/{job_id}/audio`. Pass `webhookUrl` to be notified.
| Field | Type | Description |
|---|---|---|
audio | file | Audio file (wav, mp3, ogg, flac up to 25 MB / 15 min) (wav, mp3, ogg, flac) |
effects | string | JSON string: array of effect descriptors |
output_format | string | wav (default), mp3, ogg |
curl -X POST https://ttsapi.ru/v1/audio/effects \
-H "X-Api-Key: rtt_…" \
-F "audio=@voice.mp3" \
-F 'effects=[{"type":"reverb","room_size":0.5},{"type":"pitch","semitones":2}]' \
-F "output_format=mp3"
# → 202 { "job_id": "…", "status": "queued" }
curl https://ttsapi.ru/v1/audio/effects/{job_id} -H "X-Api-Key: rtt_…"
# → 200 { "status": "completed", "result": { "format": "mp3", "duration_milliseconds": … } }
curl https://ttsapi.ru/v1/audio/effects/{job_id}/audio -H "X-Api-Key: rtt_…" --output result.mp3
POST /v1/video/effects applies effects to a video (mode=mux) or to its audio track (mode=audio) as a background job. In mux mode you can pass a separate audio file to replace the video's audio track.
Poll `GET /v1/video/effects/{job_id}` and download the result from `GET /v1/video/effects/{job_id}/file`.
| Field | Type | Description |
|---|---|---|
video | file | Video file (mp4, webm, …) |
audio | file | Optional audio file for mode=mux |
effects | string | JSON string: array of effect descriptors |
mode | string | mux (default), audio |
output_format | string | mp4, webm (mux) / wav, mp3, ogg (audio) |
curl -X POST https://ttsapi.ru/v1/video/effects \
-H "X-Api-Key: rtt_…" \
-F "video=@clip.mp4" \
-F 'effects=[{"type":"compressor","threshold_db":-18,"ratio":3}]' \
-F "mode=mux" \
-F "output_format=mp4"
# → 202 { "job_id": "…", "status": "queued" }
curl https://ttsapi.ru/v1/video/effects/{job_id}/file -H "X-Api-Key: rtt_…" --output result.mp4
POST /v1/audio/clean removes background noise and normalizes loudness of an uploaded file as a background job. With no `options` the one-click preset (denoise + normalize) is applied.
Poll `GET /v1/audio/clean/{job_id}` and download the result from `GET /v1/audio/clean/{job_id}/audio`. Pass `webhookUrl` to be notified.
| Field | Type | Description |
|---|---|---|
audio | file | Audio file (wav, mp3, ogg, flac up to 25 MB / 15 min) (wav, mp3, ogg, flac) |
options | string | JSON string with options: `denoise` (strength, stationary), `normalize` (target_db), `high_pass`, `low_pass`. Optional. |
output_format | string | wav (default), mp3, ogg |
webhookUrl | string | URL to notify when ready (query parameter) |
curl -X POST https://ttsapi.ru/v1/audio/clean \
-H "X-Api-Key: rtt_…" \
-F "audio=@recording.mp3" \
-F 'options={"denoise":{"strength":0.8,"stationary":true},"normalize":{"target_db":-1.0}}' \
-F "output_format=wav"
# → 202 { "job_id": "…", "status": "queued" }
curl https://ttsapi.ru/v1/audio/clean/{job_id} -H "X-Api-Key: rtt_…"
# → 200 { "status": "completed", "result": { "format": "wav", "duration_milliseconds": … } }
curl https://ttsapi.ru/v1/audio/clean/{job_id}/audio -H "X-Api-Key: rtt_…" --output cleaned.wav
Cleaning is also available as a preprocessor: pass `clean=true` to `/v1/transcribe`, `/v1/transcribe/sync`, `/v1/analyze`, `/v1/analyze/sync` and `/v1/voices/clone`.
GET /v1/voices returns the voice list without authentication; GET /v1/voices/{id} returns a single voice.
29 preset voices in total (default – Anna, `preset_anna`); available, for example: preset_anna, preset_maksim, preset_pavel, preset_m01, preset_w01 …
curl https://ttsapi.ru/v1/voices
POST /v1/voices/clone creates a cloned voice from 1–3 samples of the same speaker. The returned `id` is used as `voice` with `model=premium`.
One sample: wav/mp3/ogg/flac/m4a/aac up to 10 MB, clean voice without noise or music, plus its transcript (prompt_text). Cloning is available on Basic (up to 5 voices), Pro (up to 50), and Business (up to 500). Premium preset voices are available on Pro and Business.
Pass `clean=true` to automatically clean (denoise + normalize) each sample before cloning. Basic, Pro, and Business plans.
curl -X POST https://ttsapi.ru/v1/voices/clone \
-H "X-Api-Key: rtt_…" \
-F "name=my_voice" \
-F "samples=@voice_1.wav" \
-F "samples=@voice_2.wav" \
-F "samples=@voice_3.wav"
# → 201 { "id": "clone_…", "name": "my_voice", "language": "ru",
# "sample_count": 3, "created_at": "…" }
# GET /v1/voices/clone
# GET /v1/voices/clone/{id}
# DELETE /v1/voices/clone/{id} # → 204
Use the cloned voice in POST /v1/synthesize and POST /v1/synthesize/stream:
curl -X POST https://ttsapi.ru/v1/synthesize \
-H "Content-Type: application/json" \
-H "X-Api-Key: rtt_…" \
-d '{"text":"Привет!","voice":"clone_…","model":"premium","format":"mp3"}'
The async POST /v1/transcribe accepts multipart/form-data and immediately returns job_id. Fetch the result by polling GET /v1/transcribe/{job_id}.
| Field | Type | Description |
|---|---|---|
audio | file | Audio file (wav, mp3, ogg, flac up to 25 MB / 15 min) |
language | string | ISO 639-1 language code (e.g. ru, en, de). Optional – auto-detected when omitted. |
diarization | bool | Speaker separation (requires a plan with diarization) |
keyterms | string | Comma-separated terms to boost recognition (keyterm prompting) |
webhookUrl | string | URL to notify when ready |
clean | bool | Denoise + normalize the audio first (`clean=true`). Pro and Business plans. |
Supported language codes (ISO 639-1): ru, en, de, es, fr, uk, kk, uz, zh and more – Whisper supports ~99 languages in total.
# Start a job
curl -X POST https://ttsapi.ru/v1/transcribe \
-H "X-Api-Key: rtt_…" \
-F "audio=@meeting.mp3" \
-F "language=ru" \
-F "diarization=true"
# → 202 { "job_id": "…", "status": "queued" }
# Fetch result
curl https://ttsapi.ru/v1/transcribe/{job_id} \
-H "X-Api-Key: rtt_…"
For short files (up to 3 minutes) there is a synchronous option – POST /v1/transcribe/sync, which returns the result immediately.
You can pass a comma-separated `keyterms` field – the model will recognize those terms more reliably (keyterm prompting). The `segments` response now includes word-level timestamps `words` (`word`, `start`, `end`, `confidence`).
GET /v1/transcribe/{job_id}/subtitles exports a completed transcript as WebVTT or SubRip subtitles.
The `format` parameter is `vtt` (default) or `srt`.
If the job is not completed yet, `409 subtitles_unavailable` is returned.
Subtitles accept target_language (on-the-fly translation) and hot_marks=true — [fast]/[unclear] marks where a speaker talks too quickly or is hard to hear.
# VTT or SRT
curl -OJ "https://ttsapi.ru/v1/transcribe/{job_id}/subtitles?format=srt" \
-H "X-Api-Key: rtt_…"
# translated subtitles with hot marks
curl -OJ "https://ttsapi.ru/v1/transcribe/{job_id}/subtitles?format=vtt&target_language=en&hot_marks=true" \
-H "X-Api-Key: rtt_…"
POST /v1/transcribe/{job_id}/translate translates a finished transcript segment by segment (timestamps and speakers are preserved).
curl -X POST https://ttsapi.ru/v1/transcribe/{job_id}/translate \
-H "X-Api-Key: rtt_…" \
-H "Content-Type: application/json" \
-d '{"target_language": "en"}'
# → { "source_language": "ru", "target_language": "en", "transcript": "…",
# "segments": [ { "start": 0.0, "end": 2.1, "text": "…",
# "speaker": "SPEAKER_00", "confidence": 0.95 } ],
# "llm_model": "gpt-4o-mini" }
GET /v1/recordings lists, downloads, exports and shares the caller's stored recordings.
POST /v1/recordings/from-link starts a diarized transcription from a public audio URL — the server downloads the file and saves the recording with source link.
GET /v1/recordings/{id} returns a single recording's metadata; GET /v1/recordings/{id}/transcript returns the full transcript of a recording; DELETE /v1/recordings/{id} deletes a recording and its audio.
PATCH /v1/recordings/{id} updates a recording's tags and folder (an absent field is left untouched, null clears it); GET /v1/recordings/tags return the client's distinct tags and folders for autocomplete. GET /v1/recordings/folders
Share links open a public player with synced transcript highlighting; expires_in_seconds (default 7 days, max 30) and an optional password.
# list recordings (filter/pagination + tags/folders)
curl "https://ttsapi.ru/v1/recordings?source=upload&tag=sales&folder=Q3&limit=20" -H "X-Api-Key: rtt_…"
# start a transcription from a public audio URL
curl -X POST https://ttsapi.ru/v1/recordings/from-link \
-H "X-Api-Key: rtt_…" -H "Content-Type: application/json" \
-d '{"url": "https://example.com/call.mp3", "language": "ru"}'
# → 202 { "job_id": "…", "status": "queued" }
# metadata, transcript and delete
curl https://ttsapi.ru/v1/recordings/{id} -H "X-Api-Key: rtt_…"
curl https://ttsapi.ru/v1/recordings/{id}/transcript -H "X-Api-Key: rtt_…"
curl -X DELETE https://ttsapi.ru/v1/recordings/{id} -H "X-Api-Key: rtt_…"
# tags & folder (PATCH semantics: null clears, absent = untouched)
curl -X PATCH https://ttsapi.ru/v1/recordings/{id} \
-H "X-Api-Key: rtt_…" -H "Content-Type: application/json" \
-d '{"tags": ["sales", "warm"], "folder": "Q3"}'
# → { "id": "…", "folder": "Q3", "tags": ["sales", "warm"] }
# distinct tags / folders (autocomplete)
curl https://ttsapi.ru/v1/recordings/tags -H "X-Api-Key: rtt_…"
curl https://ttsapi.ru/v1/recordings/folders -H "X-Api-Key: rtt_…"
# download the stored audio
curl -OJ https://ttsapi.ru/v1/recordings/{id}/audio -H "X-Api-Key: rtt_…"
# export the transcript (txt | md | srt | vtt | docx | pdf)
curl -OJ "https://ttsapi.ru/v1/recordings/{id}/export?format=pdf" -H "X-Api-Key: rtt_…"
# create a public share link (TTL + optional password)
curl -X POST https://ttsapi.ru/v1/recordings/{id}/share \
-H "X-Api-Key: rtt_…" -H "Content-Type: application/json" \
-d '{"expires_in_seconds": 86400, "password": "secret"}'
# → { "share_id": "…", "url": "https://ttsapi.ru/share/…",
# "password_required": true, "expires_at_utc": "…" }
# list / revoke shares
curl https://ttsapi.ru/v1/recordings/{id}/share -H "X-Api-Key: rtt_…"
curl -X DELETE https://ttsapi.ru/v1/recordings/{id}/share/{token} -H "X-Api-Key: rtt_…"
# the share URL opens a player with synced transcript highlighting:
# https://ttsapi.ru/share/{token}
POST /v1/eval transcribes an uploaded clip and returns the word error rate (WER) against a reference text.
Run the same reference through different voices and synthesis settings and compare by the number. Pro/Business plans; transcription seconds are billed.
# WER: how closely the speech matches the reference text
curl -X POST https://ttsapi.ru/v1/eval \
-H "X-Api-Key: rtt_…" \
-F "audio=@synthesis.wav" \
-F "reference=Здравствуйте, это сервис синтеза речи." \
-F "normalize=true"
# → { "wer": 0.15, "accuracy": 0.85, "hits": 10,
# "substitutions": 1, "insertions": 0, "deletions": 1,
# "reference_words": 12, "hypothesis_words": 13,
# "normalized_reference": "здравствуйте это сервис синтеза речи",
# "transcript": "Здравствуйте, это сервис синтеза речи.",
# "language": "ru", "duration_seconds": 2.6 }
WS /v1/transcribe/stream streams real-time transcription over WebSocket (Pro/Business plans). Send raw PCM16 little-endian mono 16 kHz frames as binary messages; the server replies with JSON events: `session`, `vad`, `partial` and `final`.
Free and Basic plans receive `403 streaming_forbidden`.
Authentication: key in the `?api_key=…` query string or the `X-Api-Key` header (browsers cannot set headers on a WS handshake).
Client → server: binary PCM16 frames (little-endian, mono, 16 kHz) and a text `{"type":"stop"}` frame to finalize.
Server → client – JSON events:
| Type | Event | Description |
|---|---|---|
session | started / ended | Session lifecycle (+`session_id`, `audio_seconds`). |
vad | speech_started / speech_ended | Turn detection: speech start and end. |
partial | – | Interim transcript during speech. |
final | – | Final transcript of a finished utterance. |
error | – | Session error (`quota_exceeded`, `stream_failed`). |
Query parameters: `language`, `keyterms` (comma-separated), `interim=true|false`.
Quota is charged by transmitted audio (1 sec = 32,000 PCM16 bytes).
const ws = new WebSocket("wss://ttsapi.ru/v1/transcribe/stream?api_key=rtt_…&language=ru");
ws.binaryType = "arraybuffer";
ws.onmessage = (event) => console.log(JSON.parse(event.data));
ws.send(pcm16Bytes);
ws.send(JSON.stringify({ type: "stop" }));
import asyncio, json, websockets
async def main():
async with websockets.connect(
"wss://ttsapi.ru/v1/transcribe/stream?api_key=rtt_…"
) as ws:
await ws.send(open("speech.raw", "rb").read())
await ws.send(json.dumps({"type": "stop"}))
async for message in ws:
print(json.loads(message))
asyncio.run(main())
POST /v1/vad detects speech segments (Silero VAD) in an uploaded audio file and returns their start/end times. The WebSocket variant streams live `speech_started` / `speech_ended` events – the building block for voice agents.
curl -X POST https://ttsapi.ru/v1/vad \
-H "X-Api-Key: rtt_…" \
-F "audio=@meeting.mp3"
# → { "segments": [{"start": 0.1, "end": 2.4}, {"start": 3.0, "end": 5.2}],
# "speech_ratio": 0.68, "duration_seconds": 5.2, "processing_time_ms": 12 }
WS /v1/vad/stream– streaming turn detection: VAD events only, no recognition.
const ws = new WebSocket("wss://ttsapi.ru/v1/vad/stream?api_key=rtt_…");
ws.binaryType = "arraybuffer";
ws.onmessage = (event) => console.log(JSON.parse(event.data));
// ← {"type":"vad","event":"speech_started","start":1.20}
// ← {"type":"vad","event":"speech_ended","start":1.20,"end":4.85}
Convert to PCM16: `ffmpeg -i in.mp3 -ar 16000 -ac 1 -f s16le out.raw`.
The asynchronous POST /v1/analyze queues an analysis job and returns a job_id immediately; poll GET /v1/analyze/{jobId} for the result.
For short files (up to 3 minutes) use the synchronous POST /v1/analyze/sync, which returns the result immediately.
The parameter language is an ISO 639-1 language code (e.g. ru, en); when omitted, the language is auto-detected. ISO 639-1 language codes are supported.
The comma-separated `keyterms` field boosts recall of domain-specific terms.
Denoise + normalize the audio first (`clean=true`). Pro and Business plans.
# # Start a job
curl -X POST https://ttsapi.ru/v1/analyze \
-H "X-Api-Key: rtt_…" \
-F "audio=@call.mp3" \
-F "language=ru"
# → 202 { "job_id": "…", "status": "queued" }
# # Fetch result
curl https://ttsapi.ru/v1/analyze/{job_id} \
-H "X-Api-Key: rtt_…"
# Synchronous analysis
curl -X POST https://ttsapi.ru/v1/analyze/sync \
-H "X-Api-Key: rtt_…" \
-F "audio=@call.mp3" \
-F "emotions=true" \
-F "keywords=true"
# → { "transcript": "…", "segments": […], "keywords": […] }
POST /v1/analyze/topics classifies text into topics without uploading audio, returning a list of topics with relevance scores (0..1).
curl -X POST https://ttsapi.ru/v1/analyze/topics \
-H "X-Api-Key: rtt_…" \
-H "Content-Type: application/json" \
-d '{"text": "Запустили стартап и вывели продукт на рынок."}'
# → { "topics": [{"topic": "business", "score": 0.09}] }
POST /v1/analyze/summarize produces an extractive summary of text without uploading audio. The `max_sentences` parameter (1..10, default 3) controls the number of sentences.
curl -X POST https://ttsapi.ru/v1/analyze/summarize \
-H "X-Api-Key: rtt_…" \
-H "Content-Type: application/json" \
-d '{"text": "Первое предложение. Второе. Третье. Четвёртое.", "max_sentences": 2}'
# → { "summary": "…", "sentences": […] }
POST /v1/detect-language detects the language of a text snippet without uploading audio, returning the ISO 639-1 code and a confidence score (0..1).
Useful before choosing a voice or forcing a transcription language.
curl -X POST https://ttsapi.ru/v1/detect-language \
-H "X-Api-Key: rtt_…" \
-H "Content-Type: application/json" \
-d '{"text": "Привет! Как дела?"}'
# → { "language": "ru", "confidence": 0.99 }
POST /v1/redact replaces personal data in text with type labels and returns the masked text plus the found entities with their offsets. No audio upload required.
Names, organisations, locations, dates and amounts (NER) are masked, as well as phone numbers, emails and card numbers (regex).
curl -X POST https://ttsapi.ru/v1/redact \
-H "X-Api-Key: rtt_…" \
-H "Content-Type: application/json" \
-d '{"text": "Иван позвонил на +7 900 123-45-67 из Москвы."}'
# → { "redacted_text": "[PER] позвонил на [PHONE] из [LOC].", "entities": […], "count": 3 }
POST /v1/moderate checks text for profanity, insults and hate speech without uploading audio, returning a flag, an overall score (0..1) and the matched terms with their categories.
Categories: profanity, insult, hate. A lightweight lexicon classifier – a fast first-pass filter, not a full toxicity model.
curl -X POST https://ttsapi.ru/v1/moderate \
-H "X-Api-Key: rtt_…" \
-H "Content-Type: application/json" \
-d '{"text": "Это оскорбительное сообщение.", "language": "ru"}'
# → { "flagged": true, "score": 0.5, "categories": ["insult"], "matches": […] }
POST /v1/qa/evaluate evaluates a recording against a checklist of required and forbidden steps and returns a per-item verdict with evidence and a weighted 0..100 score.
Checklist items have an id, a kind (required or forbidden), a description and an optional weight. Requires the recording library and a language model – Pro/Business plans.
curl -X POST https://ttsapi.ru/v1/qa/evaluate \
-H "X-Api-Key: rtt_…" \
-H "Content-Type: application/json" \
-d '{
"recording_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"checklist": [
{ "id": "greeting", "kind": "required", "description": "Поздороваться и представиться" },
{ "id": "price", "kind": "required", "description": "Назвать цену", "weight": 2 },
{ "id": "profanity", "kind": "forbidden", "description": "Не использовать мат" }
]
}'
# → { "recording_id": "…", "score": 83.3, "passed": 2, "failed": 1,
# "items": [ { "id": "greeting", "verdict": "passed", "reason": "…",
# "quote": "Здравствуйте", "start": 0.0, "end": 2.1, "weight": 1 }, … ] }
GET /v1/qa/analytics GET /v1/qa/evaluations GET /v1/qa/evaluations/export
— aggregated analytics over saved evaluations: day-by-day score trend, top violations, score by operator and recent evaluations.
Export returns CSV (one row per call, importable into Bitrix24/amoCRM/1C) or JSON. You can pass webhook_url when evaluating — on a violation a qa.violation event is POSTed there.
curl -X GET "https://ttsapi.ru/v1/qa/analytics?days=30" \
-H "X-Api-Key: rtt_…"
# → { "total_evaluations": 120, "average_score": 84.6, "violations": 17,
# "trend": [ { "date": "2026-09-18", "average_score": 81.2, "count": 14 } ],
# "top_violations": [ … ], "by_operator": [ … ], "recent": [ … ] }
# Экспорт для CRM (CSV или JSON):
curl -X GET "https://ttsapi.ru/v1/qa/evaluations/export?format=csv&days=30" \
-H "X-Api-Key: rtt_…" -o qa.csv
POST /v1/meetings/protocol generates a structured meeting protocol – TL;DR, decisions, tasks, risks, open questions and next steps – from a recording transcript.
Optionally pick a template (standup, demo, interview, retro, one_on_one). Requires the recording library and a language model – Pro/Business plans.
curl -X POST https://ttsapi.ru/v1/meetings/protocol \
-H "X-Api-Key: rtt_…" \
-H "Content-Type: application/json" \
-d '{
"recording_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"template": "standup"
}'
# → { "recording_id": "…", "template": "standup", "tldr": "…",
# "decisions": [ { "decision": "…", "quote": "…", "start": 0.0, "end": 2.1 } ],
# "tasks": [ { "task": "…", "owner": "Иван", "deadline": "2026-09-27", "quote": "…" } ],
# "risks": [ … ], "open_questions": [ … ], "next_steps": [ … ],
# "markdown": "…", "llm_model": "gpt-4o-mini" }
POST /v1/search finds transcript passages semantically similar to a query across your recordings.
POST /v1/ask answers a question from your recordings using retrieval-augmented generation, with verbatim citations and timestamps.
Search and Q&A index your recordings automatically. Requires the recording library, embeddings and a language model – Pro/Business plans.
# search
curl -X POST https://ttsapi.ru/v1/search \
-H "X-Api-Key: rtt_…" \
-H "Content-Type: application/json" \
-d '{"query": "почему клиент отказался от Pro?", "limit": 5}'
# → { "query": "…", "hits": [ { "recording_id": "…", "text": "…",
# "speaker": "SPEAKER_00", "start": 12.0, "end": 15.5, "score": 0.92 }, … ] }
# ask (RAG)
curl -X POST https://ttsapi.ru/v1/ask \
-H "X-Api-Key: rtt_…" \
-H "Content-Type: application/json" \
-d '{"query": "почему клиент отказался от Pro?"}'
# → { "answer": "…", "citations": [ { "recording_id": "…", "quote": "…",
# "start": 12.0, "end": 15.5 } ], "llm_model": "gpt-4o-mini" }
GET /v1/recordings/{id}/speakers returns a recording's speakers with roles, statistics, a timeline and conversation metrics (interruptions, silence, speech pace).
PATCH /v1/recordings/{id}/speakers/{speakerId} renames a speaker or assigns a role (operator/client/participant).
Operator/client roles are inferred automatically (in a two-party call the operator is whoever spoke first). Overrides are stored on the recording.
# speakers, timeline and conversation metrics
curl https://ttsapi.ru/v1/recordings/a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4/speakers \
-H "X-Api-Key: rtt_…"
# → { "recording_id": "…",
# "speakers": [ { "id": "SPEAKER_00", "label": "SPEAKER_00",
# "display_name": "Иван", "role": "operator", "auto_role": "operator",
# "duration_seconds": 123.4, "speech_share": 0.62, "segment_count": 34,
# "word_count": 180, "words_per_minute": 121.3,
# "first_utterance_at": 1.2, "last_utterance_at": 210.5 }, … ],
# "timeline": [ { "start": 0.0, "end": 3.2, "speaker_id": "SPEAKER_00" }, … ],
# "metrics": { "interruptions": 4, "silence_count": 9,
# "total_silence_seconds": 11.2, "longest_silence_seconds": 4.1 } }
# rename a speaker / assign a role (either field is optional; null clears)
curl -X PATCH https://ttsapi.ru/v1/recordings/a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4/speakers/SPEAKER_00 \
-H "X-Api-Key: rtt_…" \
-H "Content-Type: application/json" \
-d '{"display_name": "Иван", "role": "operator"}'
# → { "id": "SPEAKER_00", "label": "SPEAKER_00", "display_name": "Иван",
# "role": "operator", "auto_role": "operator", … }
POST /v1/voice-id analyzes a voice recording (wav/mp3/ogg/flac up to 25 MB / 3 min) and returns a voice passport: language, gender, age group, emotional background, a speaker embedding (fingerprint) and an AI-vs-human probability.
The AI-vs-human probability is beta and is only present when the anti-spoofing detector is configured – otherwise it is null and `fake_detection_available` is false. Requires a Pro or Business plan.
curl -X POST https://ttsapi.ru/v1/voice-id \
-H "X-Api-Key: rtt_…" \
-F "audio=@recording.wav"
# → { "language": "ru", "gender": "female", "age_group": "adult",
# "emotional_background": "neutral",
# "ai_probability": 0.03, "speaker_embedding": [ … ] }
| Field | Description |
|---|---|
language | detected language and confidence (faster-whisper) |
gender | gender from the fundamental frequency (F0): `male` / `female` / `unknown` |
age_group | rough age group: `child` / `adult` / `unknown` |
emotional_background | emotional background and emotion distribution |
speaker_embedding | speaker fingerprint (ECAPA-TDNN, 192 floats); `null` if the model is unavailable |
ai_probability | probability the voice is synthesized (0..1); `null` if the detector is not configured |
fake_detection_available | `false` when the anti-spoofing model is not loaded |
nearest_voices | closest known voices (empty in the MVP – phase 1.5) |
Enroll a reference clip as a voice profile (`voice_…`), then verify a new clip against it 1:1 or identify the closest profile 1:N. `verified` is true when cosine similarity exceeds the threshold (0.70 by default).
# enroll a reference clip
curl -X POST https://ttsapi.ru/v1/voice-id/enroll \
-H "X-Api-Key: rtt_…" \
-F "audio=@speaker.wav" -F "name=Alice"
# → { "profile_id": "voice_…" }
# verify 1:1
curl -X POST https://ttsapi.ru/v1/voice-id/verify \
-H "X-Api-Key: rtt_…" \
-F "audio=@check.wav" -F "profile_id=voice_…"
# → { "profile_id": "voice_…", "similarity": 0.81, "verified": true, "threshold": 0.7 }
# identify 1:N (across your profiles)
curl -X POST https://ttsapi.ru/v1/voice-id/identify \
-H "X-Api-Key: rtt_…" \
-F "audio=@check.wav"
# → { "best_match": { "voice_id": "voice_…", "similarity": 0.81 }, "matches": […], "threshold": 0.7 }
curl https://ttsapi.ru/v1/voice-id/profiles -H "X-Api-Key: rtt_…"
curl -X DELETE https://ttsapi.ru/v1/voice-id/profiles/{profile_id} -H "X-Api-Key: rtt_…"
`enroll` stores a clip as a profile (voiceprint). Profile limits: Pro – 10, Business – 100.
`verify` (1:1) – “same person?”: `verified=true` when cosine similarity is at least the `VoiceId:SimilarityThreshold` (0.70 by default).
`identify` (1:N) – finds the closest profile among yours; narrow it down with `profile_ids`.
Cloned voices are enrolled as profiles automatically – verify against them using the clone id as `profile_id`.
Each `enroll` / `verify` / `identify` call consumes one Voice ID check from your quota (Pro – 100, Business – 1,000).
The fingerprint comes from ECAPA-TDNN (speechbrain, CPU); the anti-spoofing detector is optional and enabled on the worker via `VI_VOICEID_FAKE_MODEL`. A missing model never fails the request – the field becomes `null`/`unknown`.
POST /v1/batch/synthesize and POST /v1/batch/analyze queue batch processing: up to 20 synthesis tasks or up to 10 analysis tasks (inline base64 audio). They return `202` with a `batch_id`.
Fetch results by polling: GET /v1/batch/{batch_id}.
# Batch synthesis
curl -X POST https://ttsapi.ru/v1/batch/synthesize \
-H "Content-Type: application/json" \
-H "X-Api-Key: rtt_…" \
-d '{"items":[{"text":"Первый текст","voice":"preset_anna"},{"text":"Второй текст","voice":"preset_m01"}]}'
# → 202 { "batch_id": "…", "status": "queued", "item_count": 2 }
# Batch analysis
curl -X POST https://ttsapi.ru/v1/batch/analyze \
-H "Content-Type: application/json" \
-H "X-Api-Key: rtt_…" \
-d '{"items":[{"audio":"BASE64…","language":"ru"}]}'
# Result
curl https://ttsapi.ru/v1/batch/{batch_id} \
-H "X-Api-Key: rtt_…"
GET /v1/usage shows the remaining quota for your plan: synthesis characters, transcription minutes, and analysis requests.
curl https://ttsapi.ru/v1/usage \
-H "X-Api-Key: rtt_…"
GET /v1/billing/balance returns the key's balance, plan and recent transactions.
POST /v1/billing/topup creates a card top-up and returns a payment URL.
# balance, plan and recent transactions
curl https://ttsapi.ru/v1/billing/balance \
-H "X-Api-Key: rtt_…"
# card top-up (returns a payment URL)
curl -X POST https://ttsapi.ru/v1/billing/topup \
-H "X-Api-Key: rtt_…" -H "Content-Type: application/json" \
-d '{"amount_rub": 1000}'
# → { "transaction_id": "…", "payment_url": "…", "amount_rub": 1000, "status": "…" }
Every API key is rate-limited with a token bucket: a steady request rate plus a short burst allowance. The limit is counted per client – all keys of an account share it – and depends on the plan:
| Plan | Requests/sec | Burst |
|---|---|---|
| Free | 5 | 10 |
| Basic | 15 | 30 |
| Pro | 30 | 60 |
| Business | 60 | 120 |
Requests without a key are limited per IP: 10 requests/sec with a burst of 20. When tokens run out the request is rejected immediately – there is no waiting queue.
When exceeded, the API returns `429` with code `rate_limit_exceeded` and a `Retry-After` header – retry later with exponential backoff.
Errors are returned in RFC 7807 (Problem Details) format with an extra code.
| Code | HTTP | Description |
|---|---|---|
text_too_long | 413 | Text exceeds the limit |
text_invalid_characters | 400 | Text contains characters unsupported by the chosen voice |
quota_exceeded | 429 | Plan limit exhausted |
rate_limit_exceeded | 429 | Request rate (RPS) limit exceeded, retry later |
streaming_forbidden | 403 | Streaming is not available on the current plan |
audio_invalid | 400 | Invalid audio file |
audio_too_large | 413 | Audio file exceeds the allowed size |
job_not_found | 404 | Job not found |
engine_unavailable | 503 | Inference is temporarily unavailable |
premium_voice_forbidden | 403 | Premium engine is not available on the current plan |
clone_forbidden | 403 | Cloning is not available on the current plan |
diarization_forbidden | 403 | Diarization is not available on the current plan |
subtitles_unavailable | 409 | Subtitles unavailable: the job is not completed yet |
evaluation_forbidden | 403 | Speech evaluation is not available on the current plan |
evaluation_unavailable | 503 | Speech evaluation is temporarily unavailable |
evaluation_invalid | 400 | Invalid evaluation request (empty or too long reference) |
recording_export_invalid | 400 | Invalid export format or no transcript to export |
recording_audio_unavailable | 404 | Recording audio is no longer available (retention elapsed) |
recording_tags_invalid | 400 | Invalid tags (non-string, too long, or more than 20 tags) |
recording_folder_invalid | 400 | Invalid folder name (too long) |
share_not_found | 404 | Share link not found or revoked |
share_expired | 410 | Share link expired |
share_password_required | 403 | A password is required to access this share |
share_password_invalid | 403 | Incorrect share password |
share_invalid | 400 | Invalid share request (TTL or password) |
audio_too_long | 413 | Audio exceeds the allowed duration |
long_recording_forbidden | 403 | Long-form transcription is not available on the current plan |
call_qa_forbidden | 403 | Call QA is not available on the current plan |
call_qa_unavailable | 503 | Call QA is temporarily unavailable |
call_qa_invalid | 400 | Invalid QA request |
qa_export_invalid | 400 | Invalid QA export format — use csv or json. |
meeting_intelligence_forbidden | 403 | Meeting intelligence is not available on the current plan |
meeting_intelligence_unavailable | 503 | Meeting intelligence is temporarily unavailable |
meeting_intelligence_invalid | 400 | Invalid meeting protocol request |
search_forbidden | 403 | Semantic search is not available on the current plan |
search_unavailable | 503 | Semantic search is temporarily unavailable |
search_invalid | 400 | Invalid search query |
ask_forbidden | 403 | Q&A over recordings is not available on the current plan |
ask_unavailable | 503 | Q&A over recordings is temporarily unavailable |
ask_invalid | 400 | Invalid question |
speaker_not_found | 404 | Speaker not found in the recording |
speaker_role_invalid | 400 | Invalid speaker role (operator, client, participant, unknown) |
translation_forbidden | 403 | Translation is not available on the current plan |
translation_unavailable | 503 | Translation is temporarily unavailable |
translation_invalid | 400 | Invalid translation request (language or job) |
cleaning_invalid | 400 | Invalid audio cleaning options |
cleaning_forbidden | 403 | Audio cleaning is not available on the current plan |
cleaning_unavailable | 503 | Audio cleaning is temporarily unavailable |
effects_invalid | 400 | Invalid effects parameters |
lexicon_invalid | 400 | Invalid pronunciation lexicon |
effects_forbidden | 403 | Audio effects are not available on the current plan |
effects_unavailable | 503 | Audio effects are temporarily unavailable |
video_invalid | 400 | Invalid video file |
video_too_large | 413 | Video file exceeds the allowed size |
video_effects_invalid | 400 | Invalid video effects parameters |
video_effects_forbidden | 403 | Video effects are not available on the current plan |
video_effects_unavailable | 503 | Video effects are temporarily unavailable |
voice_id_invalid | 400 | Invalid Voice ID request |
voice_id_forbidden | 403 | Voice ID is not available on the current plan |
voice_id_unavailable | 503 | Voice ID is temporarily unavailable |
voice_profile_not_found | 404 | Voice profile not found |
voice_profile_limit_exceeded | 403 | Voice profile limit exceeded |
diarization_unavailable | 503 | Diarization is temporarily unavailable |
clone_samples_invalid | 400 | Invalid voice cloning samples |
clone_not_found | 404 | Cloned voice not found |
clone_limit_exceeded | 403 | Cloned voice limit exceeded |
recording_not_found | 404 | Recording not found |
recording_forbidden | 403 | The recording library is not available on the current plan |
recording_limit_exceeded | 403 | Recording limit exceeded |
link_invalid | 400 | Invalid audio URL |
link_unavailable | 503 | Could not download the audio from the URL |
Official Python wrapper: synthesis, streaming, transcription, analysis, text intelligence, recordings, QA, meeting intelligence, and batches.
pip install voicekit-client
PyPI → pypi.org/project/voicekit-client · GitHub → github.com/lomshakov/voicekit-python
from voicekit import VoiceKitClient
client = VoiceKitClient(api_key="rtt_…")
audio = client.synthesize("Привет! Это синтез русской речи.", voice="preset_anna", format="mp3")
open("speech.mp3", "wb").write(audio)
for chunk in client.synthesize_stream("Первое предложение. Второе."):
pass
job = client.transcribe("audio.wav", keyterms=["диагноз"])
result = client.get_transcription_job(job["job_id"])
while result["status"] not in ("completed", "failed"):
result = client.get_transcription_job(job["job_id"])
transcript = client.transcribe_sync("audio.wav")
analysis = client.analyze_sync("audio.wav")
lang = client.detect_language("Как дела?")
topics = client.topics("Нейросети и алгоритмы")
summary = client.summarize("Длинный текст.", max_sentences=3)
redacted = client.redact("Иван позвонил на +7 900 123-45-67 из Москвы.")
# Записи, QA и meeting intelligence (Pro/Business)
job = client.recording_from_link("https://example.com/call.mp3", language="ru")
speakers = client.recording_speakers("rec_…")
client.update_speaker("rec_…", "SPEAKER_00", display_name="Иван", role="operator")
client.update_recording("rec_…", tags=["sales", "warm"], folder="Q3")
pdf = client.export_recording("rec_…", format="pdf")
open("call.pdf", "wb").write(pdf)
protocol = client.meeting_protocol("rec_…", template="standup")
wer = client.evaluate("audio.wav", reference="Здравствуйте, это сервис синтеза речи.")
translated = client.translate_transcript(job["job_id"], "en")
batch = client.batch_synthesize([{"text": "Первый текст", "voice": "preset_anna"}])
status = client.get_batch(batch["batch_id"])
Options: `api_key` (required), `base_url` (default `https://ttsapi.ru`), `timeout` (120.0 s). Errors raise `VoiceKitError` with `.status`, `.code`, `.message`.
Official TypeScript/JavaScript wrapper: synthesis, streaming, transcription, analysis, text intelligence, recordings, QA, meeting intelligence, and batches.
npm install voicekit-client
npm → npmjs.com/package/voicekit-client · GitHub → github.com/lomshakov/voicekit-js
import { VoiceKitClient } from "voicekit-client";
import { writeFile } from "node:fs/promises";
const client = new VoiceKitClient({ apiKey: "rtt_…" });
const audio = await client.synthesize("Привет! Это синтез русской речи.", {
voice: "preset_anna",
format: "mp3",
});
await writeFile("speech.mp3", audio);
for await (const chunk of client.synthesizeStream("Первое предложение. Второе.")) {
// …
}
const job = await client.transcribe("audio.wav", { keyterms: ["диагноз"] });
let result = await client.getTranscriptionJob(job.job_id);
while (!["completed", "failed"].includes(result.status)) {
await new Promise((r) => setTimeout(r, 1000));
result = await client.getTranscriptionJob(job.job_id);
}
const transcript = await client.transcribeSync("audio.wav");
const analysis = await client.analyzeSync("audio.wav");
const lang = await client.detectLanguage("Как дела?");
const topics = await client.topics("Нейросети и алгоритмы");
const summary = await client.summarize("Длинный текст.", undefined, 3);
const redacted = await client.redact("Иван позвонил на +7 900 123-45-67 из Москвы.");
// Recordings, QA and meeting intelligence (Pro/Business)
const linkJob = await client.recordingFromLink("https://example.com/call.mp3", "ru");
const speakers = await client.recordingSpeakers("rec_…");
await client.updateSpeaker("rec_…", "SPEAKER_00", { displayName: "Иван", role: "operator" });
await client.updateRecording("rec_…", { tags: ["sales", "warm"], folder: "Q3" });
const pdf = await client.exportRecording("rec_…", "pdf");
const protocol = await client.meetingProtocol("rec_…", "standup");
const wer = await client.evaluate("audio.wav", "Здравствуйте, это сервис синтеза речи.");
const translated = await client.translateTranscript(linkJob.job_id, "en");
const batch = await client.batchSynthesize([{ text: "Первый текст", voice: "preset_anna" }]);
const status = await client.getBatch(batch.batch_id);
Options: `apiKey` (required), `baseUrl` (default `https://ttsapi.ru`), `timeoutMs` (120000). Errors reject with `VoiceKitError` (`.status`, `.code`, `.message`).
WebSocket endpoints (streaming STT, VAD) are consumed directly with a WS client – see the sections above.
Official C#/.NET wrapper: synthesis, streaming, transcription, analysis, text intelligence, recordings, QA, meeting intelligence, and batches. No external dependencies – BCL only.
dotnet add package VoiceKit.Client
NuGet → nuget.org/packages/VoiceKit.Client · GitHub → github.com/lomshakov/voicekit-dotnet
using VoiceKit;
using var client = new VoiceKitClient("rtt_…");
byte[] audio = await client.SynthesizeAsync("Привет! Это синтез русской речи.", voice: "preset_anna", format: "mp3");
await File.WriteAllBytesAsync("speech.mp3", audio);
await foreach (var chunk in client.SynthesizeStreamAsync("Первое предложение. Второе."))
{
// …
}
var job = await client.TranscribeAsync("audio.wav", keyterms: new[] { "диагноз" });
var result = await client.GetTranscriptionJobAsync(job!["job_id"]!.GetValue<string>());
while (result!["status"]!.GetValue<string>() is not ("completed" or "failed"))
{
await Task.Delay(1000);
result = await client.GetTranscriptionJobAsync(job!["job_id"]!.GetValue<string>());
}
var transcript = await client.TranscribeSyncAsync("audio.wav");
var analysis = await client.AnalyzeSyncAsync("audio.wav");
var lang = await client.DetectLanguageAsync("Как дела?");
var topics = await client.TopicsAsync("Нейросети и алгоритмы");
var summary = await client.SummarizeAsync("Длинный текст.", maxSentences: 3);
var redacted = await client.RedactAsync("Иван позвонил на +7 900 123-45-67 из Москвы.");
// Записи, QA и meeting intelligence (Pro/Business)
var linkJob = await client.StartRecordingFromLinkAsync("https://example.com/call.mp3", language: "ru");
var speakers = await client.GetRecordingSpeakersAsync("rec_…");
await client.UpdateRecordingSpeakerAsync("rec_…", "SPEAKER_00", displayName: "Иван", role: "operator");
await client.UpdateRecordingAsync("rec_…", tags: new[] { "sales", "warm" }, setTags: true, folder: "Q3", setFolder: true);
byte[] pdf = await client.ExportRecordingAsync("rec_…", "pdf");
var protocol = await client.MeetingProtocolAsync("rec_…", template: "standup");
var wer = await client.EvaluateAsync("audio.wav", reference: "Здравствуйте, это сервис синтеза речи.");
var translated = await client.TranslateTranscriptAsync(linkJob!["job_id"]!.GetValue<string>(), "en");
var batch = await client.BatchSynthesizeAsync(new object[] { new { text = "Первый текст", voice = "preset_anna" } });
var status = await client.GetBatchAsync(batch!["batch_id"]!.GetValue<string>());
Options: `apiKey` (required), `baseUrl` (default `https://ttsapi.ru`), `timeout` (120 s). Errors throw `VoiceKitError` with `.Status`, `.Code`, `.Message`. JSON responses are returned as `System.Text.Json.Nodes.JsonNode`.
WebSocket endpoints (streaming STT, VAD) are consumed directly with a WS client – see the sections above.
Official Go wrapper: synthesis, streaming, transcription, analysis, text intelligence, recordings, QA, meeting intelligence and batches. Standard library plus a WebSocket client only; Go 1.22+.
go get github.com/lomshakov/voicekit-go
pkg.go.dev → github.com/lomshakov/voicekit-go · GitHub → github.com/lomshakov/voicekit-go
client, err := voicekit.NewClient("rtt_…")
ctx := context.Background()
audio, err := client.Synthesize(ctx, "Привет! Это синтез русской речи.", &voicekit.SynthesizeOptions{
Voice: "preset_anna",
Format: "mp3",
})
os.WriteFile("speech.mp3", audio, 0o644)
rc, err := client.SynthesizeStream(ctx, "Первое предложение. Второе.", nil)
defer rc.Close()
job, err := client.Transcribe(ctx, voicekit.Bytes(audio), &voicekit.TranscribeOptions{Keyterms: []string{"диагноз"}})
result, err := client.GetTranscriptionJob(ctx, job.Str("job_id"))
// опрашивайте, пока result.Str("status") не станет completed или failed
transcript, err := client.TranscribeSync(ctx, voicekit.Bytes(audio), nil)
analysis, err := client.AnalyzeSync(ctx, voicekit.Bytes(audio), nil)
lang, err := client.DetectLanguage(ctx, "Как дела?")
topics, err := client.Topics(ctx, "Нейросети и алгоритмы", "")
summary, err := client.Summarize(ctx, "Длинный текст.", &voicekit.SummarizeOptions{MaxSentences: 3})
redacted, err := client.Redact(ctx, "Иван позвонил на +7 900 123-45-67 из Москвы.", "")
// Записи, QA и meeting intelligence (Pro/Business)
linkJob, err := client.RecordingFromLink(ctx, "https://example.com/call.mp3", "ru")
speakers, err := client.RecordingSpeakers(ctx, "rec_…")
client.UpdateSpeaker(ctx, "rec_…", "SPEAKER_00", "Иван", "operator")
folder := "Q3"
client.UpdateRecording(ctx, "rec_…", voicekit.UpdateRecordingOptions{Tags: []string{"sales", "warm"}, Folder: &folder})
pdf, err := client.ExportRecording(ctx, "rec_…", "pdf")
protocol, err := client.MeetingProtocol(ctx, "rec_…", "standup")
wer, err := client.Evaluate(ctx, voicekit.Bytes(audio), "Здравствуйте, это сервис синтеза речи.", nil)
translated, err := client.TranslateTranscript(ctx, linkJob.Str("job_id"), "en")
batch, err := client.BatchSynthesize(ctx, []map[string]any{{"text": "Первый текст", "voice": "preset_anna"}})
status, err := client.GetBatch(ctx, batch.Str("batch_id"))
Options: `voicekit.NewClient(apiKey, opts…)` — the key is required; `voicekit.WithBaseURL` (default `https://ttsapi.ru`), `voicekit.WithTimeout` (120 s). Errors are `*voicekit.APIError` with `.Status`, `.Code`, `.Message`; check with `errors.Is(err, voicekit.ErrForbidden)` or `voicekit.ErrorCode(err)`. JSON responses come back as `voicekit.Object` with `.Str()`, `.Int()`, `.Bool()` and `.List()` accessors.
WebSocket endpoints (streaming STT, VAD) are consumed directly with a WS client – see the sections above.
Official PHP client: synthesis, streaming, transcription, analysis, text intelligence, recordings, QA, meeting intelligence and batches. Dependency-free: PHP 8.1+, cURL or streams, WebSocket sessions over raw sockets.
composer require lomshakov/voicekit-client
Packagist → lomshakov/voicekit-client · GitHub → github.com/lomshakov/voicekit-php
use VoiceKit\VoiceKitClient;
$client = new VoiceKitClient("rtt_…");
$audio = $client->synthesize("Привет! Это синтез русской речи.", voice: "preset_anna", format: "mp3");
file_put_contents("speech.mp3", $audio);
$out = fopen("long.mp3", "wb");
foreach ($client->synthesizeStream($longText, voice: "preset_anna") as $chunk) {
fwrite($out, $chunk); // чанки приходят по мере генерации
}
fclose($out);
$job = $client->transcribe("audio.wav", language: "ru", keyterms: ["диагноз"]);
$result = $client->getTranscriptionJob($job->str("job_id"));
// опрашивайте, пока $result->str("status") не станет completed или failed
$transcript = $client->transcribeSync("audio.wav");
$analysis = $client->analyzeSync("audio.wav");
$lang = $client->detectLanguage("Как дела?");
$topics = $client->topics("Нейросети и алгоритмы");
$summary = $client->summarize("Длинный текст.", maxSentences: 3);
$redacted = $client->redact("Иван позвонил на +7 900 123-45-67 из Москвы.");
// Записи, QA и meeting intelligence (Pro/Business)
$linkJob = $client->recordingFromLink("https://example.com/call.mp3", "ru");
$speakers = $client->recordingSpeakers("rec_…");
$client->updateSpeaker("rec_…", "SPEAKER_00", displayName: "Иван", role: "operator");
$client->updateRecording("rec_…", tags: ["sales", "warm"], folder: "Q3");
$pdf = $client->exportRecording("rec_…", "pdf");
$protocol = $client->meetingProtocol("rec_…", "standup");
$wer = $client->evaluate("audio.wav", "Здравствуйте, это сервис синтеза речи.");
$translated = $client->translateTranscript($linkJob->str("job_id"), "en");
$batch = $client->batchSynthesize([["text" => "Первый текст", "voice" => "preset_anna"]]);
$status = $client->getBatch($batch->str("batch_id"));
Options: `new VoiceKitClient(apiKey)` — the key is required, then named arguments `baseUrl` (default `https://ttsapi.ru`), `timeout` (120 s), `transport`, `headers`. Errors are `VoiceKitError` with `.statusCode()`, `.errorCode()`, `.isForbidden()` and `.isCode('quota_exceeded')`. JSON responses come back as `VoiceKit\Result` with `.str()`, `.int()`, `.float()`, `.bool()` and `.list()` accessors — array access works too: `$result['status']`.
WebSocket endpoints (streaming STT, VAD) are consumed directly with a WS client – see the sections above.
Official Java client: synthesis, streaming, transcription, analysis, text intelligence, recordings, QA, meeting intelligence and batches. No runtime dependencies: JDK 17+, HTTP over java.net.http, WebSocket sessions with the built-in client.
<dependency>
<groupId>io.github.lomshakov</groupId>
<artifactId>voicekit-client</artifactId>
<version>0.4.0</version>
</dependency>
// Gradle
implementation("io.github.lomshakov:voicekit-client:0.4.0")
Maven Central → io.github.lomshakov:voicekit-client · GitHub → github.com/lomshakov/voicekit-java
import io.github.lomshakov.voicekit.*;
VoiceKitClient client = new VoiceKitClient("rtt_…");
byte[] audio = client.synthesize("Привет! Это синтез русской речи.", new SynthesizeOptions()
.voice("preset_anna")
.format("mp3"));
Files.write(Path.of("speech.mp3"), audio);
try (InputStream stream = client.synthesizeStream("Первое предложение. Второе.", null)) {
Files.copy(stream, Path.of("long.mp3"), StandardCopyOption.REPLACE_EXISTING);
}
Result job = client.transcribe(FilePart.ofPath("speech.mp3"), new TranscribeOptions()
.language("ru")
.keyterms(List.of("диагноз")));
Result transcript = client.transcriptionJob(job.getString("job_id"));
// опрашивайте, пока transcript.getString("status") не станет completed или failed
Result inline = client.transcribeSync(FilePart.ofPath("speech.mp3"));
Result analysis = client.analyzeSync(FilePart.ofPath("speech.mp3"));
String lang = client.detectLanguage("Как дела?").getString("language");
Result topics = client.topics("Нейросети и алгоритмы");
Result summary = client.summarize("Длинный текст.", new SummarizeOptions().maxSentences(3));
Result redacted = client.redact("Иван позвонил на +7 900 123-45-67 из Москвы.", "ru");
// Записи, QA и meeting intelligence (Pro/Business)
Result linkJob = client.recordingFromLink("https://example.com/call.mp3", "ru");
Result speakers = client.recordingSpeakers("rec_…");
client.updateSpeaker("rec_…", "SPEAKER_00", "Иван", "operator");
client.updateRecording("rec_…", List.of("sales", "warm"), "Q3");
byte[] pdf = client.exportRecording("rec_…", "pdf");
Result protocol = client.meetingProtocol("rec_…", "standup");
Result wer = client.evaluate(FilePart.ofPath("speech.mp3"), "Здравствуйте, это сервис синтеза речи.", null);
Result translated = client.translateTranscript(linkJob.getString("job_id"), "en");
Result batch = client.batchSynthesize(List.of(Map.of("text", "Первый текст", "voice", "preset_anna")));
Result status = client.batch(batch.getString("batch_id"));
Options: `new VoiceKitClient(apiKey)` — the key is required, then `baseUrl` (default `https://ttsapi.ru`), `timeout` (120 s), `transport`, `headers`. Errors are `VoiceKitException` with `.statusCode()`, `.errorCode()`, `.isForbidden()` and `.isCode('quota_exceeded')`. JSON responses come back as `Result` with `.getString()`, `.getInt()`, `.getDouble()`, `.getBoolean()` and `.getList()` accessors.
WebSocket endpoints (streaming STT, VAD) are consumed directly with a WS client – see the sections above.