Documentation

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)

Quick start

  1. Sign up and get an API key in the form rtt_… in your dashboard.
  2. Send the key in the X-Api-Key header of every request.
  3. Base URL – 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

Authentication

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.

API keys

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.

Access scopes

A scope restricts which endpoints a key may call. By default a key is created without restrictions (all scopes); plan limits still apply separately.

ScopeDescription
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`.

Expiry and per-key limits

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`.

Per-key usage

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.

Managing keys

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.

Speech synthesis

POST /v1/synthesize accepts JSON and returns a binary audio file (mp3, wav, or ogg).

FieldTypeDescription
textstringText to synthesize (up to 5000 characters)
voicestringVoice ID, e.g. preset_anna
formatstringmp3 (default), wav, ogg
sample_rateintSample rate, e.g. 24000
speedfloatSpeech speed (default 1.0)
languagestringSynthesis language (defaults to the voice language): ru, en, zh, ja, ko, de, es, fr, it Defaults to the voice language when omitted.
put_accentboolAccepted for backward compatibility; does not affect synthesis
put_yoboolRestore the letter ё: accepted for backward compatibility and ignored
normalizeboolSpell numbers out in words: accepted for backward compatibility and ignored — the engine already reads digits correctly
lexiconobjectPronunciation dictionary: key – as written in the text, value – how to say it. Whole-word, case-insensitive substitution, up to 200 entries. Example: {"ООО": "общество с ограниченной ответственностью"}
effectsstringOptional 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

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

Python

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)

JavaScript

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()))

C# (.NET)

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());

Chunk streaming

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

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

Python

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.

Long-form synthesis

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.

FieldTypeDescription
textstringText to synthesize (up to 5000 characters)
voicestringVoice ID, e.g. preset_anna
formatstringwav (default),
sample_rateintSample rate, e.g. 24000
speedfloatSpeech speed (default 1.0)
languagestringSynthesis language (defaults to the voice language):

cURL

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

Python

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)

Audio effects

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.

FieldTypeDescription
audiofileAudio file (wav, mp3, ogg, flac up to 25 MB / 15 min) (wav, mp3, ogg, flac)
effectsstringJSON string: array of effect descriptors
output_formatstringwav (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

Video effects

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`.

FieldTypeDescription
videofileVideo file (mp4, webm, …)
audiofileOptional audio file for mode=mux
effectsstringJSON string: array of effect descriptors
modestringmux (default), audio
output_formatstringmp4, 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

Audio cleaning

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.

FieldTypeDescription
audiofileAudio file (wav, mp3, ogg, flac up to 25 MB / 15 min) (wav, mp3, ogg, flac)
optionsstringJSON string with options: `denoise` (strength, stationary), `normalize` (target_db), `high_pass`, `low_pass`. Optional.
output_formatstringwav (default), mp3, ogg
webhookUrlstringURL 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`.

Voices

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

Voice cloning

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"}'

Transcription

The async POST /v1/transcribe accepts multipart/form-data and immediately returns job_id. Fetch the result by polling GET /v1/transcribe/{job_id}.

FieldTypeDescription
audiofileAudio file (wav, mp3, ogg, flac up to 25 MB / 15 min)
languagestringISO 639-1 language code (e.g. ru, en, de). Optional – auto-detected when omitted.
diarizationboolSpeaker separation (requires a plan with diarization)
keytermsstringComma-separated terms to boost recognition (keyterm prompting)
webhookUrlstringURL to notify when ready
cleanboolDenoise + 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`).

Subtitles

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_…"

Transcript translation

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" }

Recording library

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}

Speech evaluation

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 }

Streaming STT

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:

TypeEventDescription
sessionstarted / endedSession lifecycle (+`session_id`, `audio_seconds`).
vadspeech_started / speech_endedTurn 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).

JavaScript

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" }));

Python

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())

VAD / turn detection

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`.

Audio analysis

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": […] }

Topics

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}] }

Summarization

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": […] }

Language detection

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 }

PII redaction

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 }

Moderation

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": […] }

Call QA

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 }, … ] }

QA analytics

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

Meeting intelligence

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" }

Speakers & roles

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", … }

Voice ID

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": [ … ] }

Voice passport fields

FieldDescription
languagedetected language and confidence (faster-whisper)
gendergender from the fundamental frequency (F0): `male` / `female` / `unknown`
age_grouprough age group: `child` / `adult` / `unknown`
emotional_backgroundemotional background and emotion distribution
speaker_embeddingspeaker fingerprint (ECAPA-TDNN, 192 floats); `null` if the model is unavailable
ai_probabilityprobability 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_voicesclosest known voices (empty in the MVP – phase 1.5)

Voice verification (enroll → verify / identify)

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).

Models and limitations

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`.

Batch

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_…"

Quotas and limits

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_…"

Billing

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": "…" }

Rate limits (RPS)

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:

PlanRequests/secBurst
Free510
Basic1530
Pro3060
Business60120

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

Errors are returned in RFC 7807 (Problem Details) format with an extra code.

CodeHTTPDescription
text_too_long413Text exceeds the limit
text_invalid_characters400Text contains characters unsupported by the chosen voice
quota_exceeded429Plan limit exhausted
rate_limit_exceeded429Request rate (RPS) limit exceeded, retry later
streaming_forbidden403Streaming is not available on the current plan
audio_invalid400Invalid audio file
audio_too_large413Audio file exceeds the allowed size
job_not_found404Job not found
engine_unavailable503Inference is temporarily unavailable
premium_voice_forbidden403Premium engine is not available on the current plan
clone_forbidden403Cloning is not available on the current plan
diarization_forbidden403Diarization is not available on the current plan
subtitles_unavailable409Subtitles unavailable: the job is not completed yet
evaluation_forbidden403Speech evaluation is not available on the current plan
evaluation_unavailable503Speech evaluation is temporarily unavailable
evaluation_invalid400Invalid evaluation request (empty or too long reference)
recording_export_invalid400Invalid export format or no transcript to export
recording_audio_unavailable404Recording audio is no longer available (retention elapsed)
recording_tags_invalid400Invalid tags (non-string, too long, or more than 20 tags)
recording_folder_invalid400Invalid folder name (too long)
share_not_found404Share link not found or revoked
share_expired410Share link expired
share_password_required403A password is required to access this share
share_password_invalid403Incorrect share password
share_invalid400Invalid share request (TTL or password)
audio_too_long413Audio exceeds the allowed duration
long_recording_forbidden403Long-form transcription is not available on the current plan
call_qa_forbidden403Call QA is not available on the current plan
call_qa_unavailable503Call QA is temporarily unavailable
call_qa_invalid400Invalid QA request
qa_export_invalid400Invalid QA export format — use csv or json.
meeting_intelligence_forbidden403Meeting intelligence is not available on the current plan
meeting_intelligence_unavailable503Meeting intelligence is temporarily unavailable
meeting_intelligence_invalid400Invalid meeting protocol request
search_forbidden403Semantic search is not available on the current plan
search_unavailable503Semantic search is temporarily unavailable
search_invalid400Invalid search query
ask_forbidden403Q&A over recordings is not available on the current plan
ask_unavailable503Q&A over recordings is temporarily unavailable
ask_invalid400Invalid question
speaker_not_found404Speaker not found in the recording
speaker_role_invalid400Invalid speaker role (operator, client, participant, unknown)
translation_forbidden403Translation is not available on the current plan
translation_unavailable503Translation is temporarily unavailable
translation_invalid400Invalid translation request (language or job)
cleaning_invalid400Invalid audio cleaning options
cleaning_forbidden403Audio cleaning is not available on the current plan
cleaning_unavailable503Audio cleaning is temporarily unavailable
effects_invalid400Invalid effects parameters
lexicon_invalid400Invalid pronunciation lexicon
effects_forbidden403Audio effects are not available on the current plan
effects_unavailable503Audio effects are temporarily unavailable
video_invalid400Invalid video file
video_too_large413Video file exceeds the allowed size
video_effects_invalid400Invalid video effects parameters
video_effects_forbidden403Video effects are not available on the current plan
video_effects_unavailable503Video effects are temporarily unavailable
voice_id_invalid400Invalid Voice ID request
voice_id_forbidden403Voice ID is not available on the current plan
voice_id_unavailable503Voice ID is temporarily unavailable
voice_profile_not_found404Voice profile not found
voice_profile_limit_exceeded403Voice profile limit exceeded
diarization_unavailable503Diarization is temporarily unavailable
clone_samples_invalid400Invalid voice cloning samples
clone_not_found404Cloned voice not found
clone_limit_exceeded403Cloned voice limit exceeded
recording_not_found404Recording not found
recording_forbidden403The recording library is not available on the current plan
recording_limit_exceeded403Recording limit exceeded
link_invalid400Invalid audio URL
link_unavailable503Could not download the audio from the URL

Python SDK

Official Python wrapper: synthesis, streaming, transcription, analysis, text intelligence, recordings, QA, meeting intelligence, and batches.

Install

pip install voicekit-client

PyPI → pypi.org/project/voicekit-client · GitHub → github.com/lomshakov/voicekit-python

Quick start

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`.

TypeScript SDK

Official TypeScript/JavaScript wrapper: synthesis, streaming, transcription, analysis, text intelligence, recordings, QA, meeting intelligence, and batches.

Install

npm install voicekit-client

npm → npmjs.com/package/voicekit-client · GitHub → github.com/lomshakov/voicekit-js

Quick start

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.

C# SDK

Official C#/.NET wrapper: synthesis, streaming, transcription, analysis, text intelligence, recordings, QA, meeting intelligence, and batches. No external dependencies – BCL only.

Install

dotnet add package VoiceKit.Client

NuGet → nuget.org/packages/VoiceKit.Client · GitHub → github.com/lomshakov/voicekit-dotnet

Quick start

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.

Go SDK

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+.

Install

go get github.com/lomshakov/voicekit-go

pkg.go.dev → github.com/lomshakov/voicekit-go · GitHub → github.com/lomshakov/voicekit-go

Quick start

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.

PHP SDK

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.

Install

composer require lomshakov/voicekit-client

Packagist → lomshakov/voicekit-client · GitHub → github.com/lomshakov/voicekit-php

Quick start

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.

Java SDK

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.

Install

<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

Quick start

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.