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.
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. |
normalize | bool | Spell numbers out in words (true by default) |
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
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 ttsapi import RussianTtsClient
client = RussianTtsClient(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
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). The premium CosyVoice engine is available on 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 |
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.
curl -OJ https://ttsapi.ru/v1/transcribe/{job_id}/subtitles?format=srt \
-H "X-Api-Key: rtt_…"
POST /v1/analyze returns the transcript, per-segment emotions, and keywords.
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.
curl -X POST https://ttsapi.ru/v1/analyze \
-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": […] }
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`.
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_…"
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 |
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 |
audio_too_long | 413 | Audio exceeds the allowed duration |
Official Python wrapper: synthesis, streaming, transcription, analysis, text intelligence, and batches.
pip install ttsapi-client
from ttsapi import RussianTtsClient
client = RussianTtsClient(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 из Москвы.")
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 `RussianTtsError` with `.status`, `.code`, `.message`.
Official TypeScript/JavaScript wrapper. Zero dependencies, Node.js 18+.
npm install ttsapi-client
import { RussianTtsClient } from "ttsapi-client";
import { writeFile } from "node:fs/promises";
const client = new RussianTtsClient({ 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 из Москвы.");
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 `RussianTtsError` (`.status`, `.code`, `.message`).
WebSocket endpoints (streaming STT, VAD) are consumed directly with a WS client — see the sections above.