Skip to main content
Documentation

Quickstart.

From signup to first synthesized audio in under 60 seconds.

1. Get an API key

Sign in with Google, then go to API Keys in your dashboard and create a new key. The full key is shown only once at creation, so copy it somewhere safe before closing the dialog.

Generate an API key

2. Pick a model

Every request takes a model and text. Each model is steered differently — muga by a global tone, mulberry by a natural-language description:

ModelBest for
silk muga 1
muga
Ultra–low-latency streaming. Set the delivery with a global tone — prefix your text with [neutral], [happy], [sad], [excited], [angry], or [whisper].
silk mulberry 1.5
mulberry
Expressive instruct-TTS. Steer with a rich natural-language description, or pick one of our preset speaker voices with speaker and tune its f0_up_key pitch (see below).

mulberry only: set speaker to one of Emma, Mia, Sophia, Ava, speaker_1speaker_4, Lucas, Noah, Theo or Adam to use one of twelve fixed studio voices, and f0_up_key to shift its pitch by −12…+12 semitones. Omit speaker to use the voice described by description.

3. Synthesize speech

Pass your key as a Bearer token. The response body is a 24 kHz mono WAV (audio/wav). Quickest smoke test:

bash
curl -X POST https://silk-api.rumik.ai/v1/tts \
  -H "Authorization: Bearer rk_live_•••••••••" \
  -H "Content-Type: application/json" \
  -d '{ "model": "muga", "text": "[happy] Hello, world." }' \
  --output speech.wav

In Python, for both models:

python
import requests

API_KEY = "rk_live_•••••••••"
BASE = "https://silk-api.rumik.ai"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}

# muga — fast voice, tone set via a [tone] prefix on the text
# tones: neutral (default), happy, sad, excited, angry, whisper
r = requests.post(f"{BASE}/v1/tts", headers=HEADERS, json={
    "model": "muga",
    "text": "[happy] Hello, world.",
})
r.raise_for_status()
open("muga.wav", "wb").write(r.content)        # 24 kHz mono WAV

# mulberry — expressive instruct-TTS, steered by a description
# (+ optional preset speaker voice)
r = requests.post(f"{BASE}/v1/tts", headers=HEADERS, json={
    "model": "mulberry",
    "text": "Welcome to the future of synthetic speech.",
    "description": "warm, upbeat narrator",
    "speaker": "Emma",        # optional preset voice: Emma/Mia/Sophia/Ava, speaker_1..speaker_4, Lucas/Noah/Theo/Adam
    "f0_up_key": 0,           # optional pitch shift in semitones (-12..12)
})
r.raise_for_status()
open("mulberry.wav", "wb").write(r.content)

4. Stream in real time

For real-time playback, mint a one-shot WebSocket session, connect to it, and send a JSON frame with your synthesis parameters. The server streams raw PCM int16 little-endian @ 24 kHz mono as binary frames, then a terminal {"type":"done"} JSON text frame.

Python (using websockets):

python
import asyncio, json, wave, requests, websockets

API_KEY = "rk_live_•••••••••"
BASE = "https://silk-api.rumik.ai"

async def main():
    # 1. Mint a one-shot WS session -> { ws_url, token }
    s = requests.post(f"{BASE}/v1/tts/ws-connect",
                      headers={"Authorization": f"Bearer {API_KEY}"},
                      json={"model": "mulberry", "text": "Streaming in real time."}).json()

    # 2. Connect, then send the synthesis frame
    async with websockets.connect(f'{s["ws_url"]}?token={s["token"]}') as ws:
        await ws.send(json.dumps({
            "text": "Streaming in real time.",
            "description": "calm female narrator",
            "speaker": "speaker_1",   # mulberry only; omit for muga / the described voice
            "f0_up_key": 0,
        }))

        # 3. Collect PCM int16 (24 kHz mono) until the done frame
        pcm = bytearray()
        async for msg in ws:
            if isinstance(msg, bytes):
                pcm.extend(msg)
            elif json.loads(msg).get("type") == "done":
                break

    with wave.open("stream.wav", "wb") as w:
        w.setnchannels(1); w.setsampwidth(2); w.setframerate(24000)
        w.writeframes(pcm)

asyncio.run(main())

HTML — play it straight in the browser with the Web Audio API:

html
<!doctype html>
<html>
  <body>
    <button id="play">Speak</button>
    <script>
      const API_KEY = "rk_live_•••••••••";   // use a key with the tts:stream scope
      const BASE = "https://silk-api.rumik.ai";

      document.getElementById("play").onclick = async () => {
        // 1. Mint a one-shot WebSocket session -> { ws_url, token }
        const res = await fetch(BASE + "/v1/tts/ws-connect", {
          method: "POST",
          headers: { "Authorization": "Bearer " + API_KEY, "Content-Type": "application/json" },
          body: JSON.stringify({ model: "mulberry", text: "Hello from the browser." }),
        });
        const { ws_url, token } = await res.json();

        // 2. Set up 24 kHz mono playback
        const ctx = new AudioContext({ sampleRate: 24000 });
        let playAt = ctx.currentTime;

        // 3. Connect, send the synthesis frame, queue PCM as it arrives
        const ws = new WebSocket(ws_url + "?token=" + encodeURIComponent(token));
        ws.binaryType = "arraybuffer";

        ws.onopen = () => ws.send(JSON.stringify({
          text: "Hello from the browser.",
          description: "warm, friendly narrator",
          speaker: "speaker_1",   // mulberry only; omit for muga / the described voice
          f0_up_key: 0,           // pitch shift in semitones (-12..12)
        }));

        ws.onmessage = (e) => {
          if (e.data instanceof ArrayBuffer) {
            const pcm = new Int16Array(e.data);
            const buf = ctx.createBuffer(1, pcm.length, 24000);
            const ch = buf.getChannelData(0);
            for (let i = 0; i < pcm.length; i++) ch[i] = pcm[i] / 32768;
            const src = ctx.createBufferSource();
            src.buffer = buf;
            src.connect(ctx.destination);
            playAt = Math.max(playAt, ctx.currentTime);
            src.start(playAt);
            playAt += buf.duration;
          } else if (JSON.parse(e.data).type === "done" || JSON.parse(e.data).error) {
            ws.close();
          }
        };
      };
    </script>
  </body>
</html>

Request fields

FieldDefaultNotes
textRequired. Up to 2000 characters. For muga, prefix with a tone tag, e.g. [happy].
modelmugamuga or mulberry.
descriptionmulberry only. Natural-language voice description.
speakermulberry only. Preset voice: Emma, Mia, Sophia, Ava, speaker_1..speaker_4, Lucas, Noah, Theo or Adam. Omit to use description.
f0_up_key0mulberry only. Pitch shift in semitones, -12..12. Applied with speaker.
temperature0.6Sampling temperature.
top_p0.95Nucleus sampling.
top_k50Top-k sampling.
repetition_penalty1.2Penalize repeated tokens.
max_new_tokens2048Output length cap.

Errors and rate limits

All errors return JSON of shape { error, code }. RPM limits are enforced per API key, while concurrent request limits are shared across the account. The response includes a Retry-After header when applicable.

StatusMeaning
400Malformed request body or unknown model
401Bearer token missing, invalid, or expired
403Key revoked or account disabled
422Validation failed. Check the error field
429Rate limit hit. See Retry-After
503Upstream temporarily unavailable. Retry shortly