Fish Audio Docs
API ReferenceText to SpeechRealtime Text to Speech

Realtime Text to Speech v1

Maintain clients that use the frozen Realtime TTS WebSocket v1 protocol.

Realtime TTS v1 is a frozen compatibility protocol for existing clients. New integrations should use the parent v3 contract. Do not migrate a v1 client by changing only the URL: v2 and v3 use different event and request contracts.

Endpoint and protocol

ItemValue
WebSocketwss://realtime.fishaudio.org/v1/tts/live
Subprotocolrealtime.tts.msgpack.v1
EncodingMessagePack binary frames

Authenticate from a trusted backend during the WebSocket handshake:

Authorization: Bearer FISHAUDIO_API_KEY
Sec-WebSocket-Protocol: realtime.tts.msgpack.v1

Do not place credentials in the URL or expose a long-lived API key to browser code.

Lifecycle

connect → authenticated → start → ready
        → text → flush → audio (one or more) → usage
        → [text → flush → audio → usage] → stop → finish

Send every application message as a MessagePack map. One connection represents one session. A flush generates the currently buffered text; you may submit more segments before stop.

Client events

EventRequired fieldsNotes
startevent, requestStarts the session after authenticated.
textevent, textAdds non-empty UTF-8 text to the current segment.
flusheventGenerates the buffered segment; an empty segment is rejected.
stopeventFlushes remaining text, completes the session, and returns finish.
pingeventOptional timestamp; the server returns pong.

Each event may include an optional request_id string of up to 128 characters.

start.request

FieldRequiredConstraints
model_idYesPublic voice/model UUID used by the v1 client.
engine_model_idNoLegacy engine selector, when assigned.
formatNomp3, wav, pcm, or opus; default mp3.
speedNo0.5 to 2.
volumeNo0 to 10.
languageNoLanguage hint, 1–32 characters.
chunk_lengthNoInteger from 100 to 300.
latencyNonormal or balanced.

Legacy camelCase aliases modelId and engineModelId are also accepted. Unknown fields are rejected.

Server events

Handle authenticated, ready, audio, usage, pong, warning, finish, and error. Audio bytes are returned in audio.data. The usage event reports segment units and remaining quota; finish.units reports accepted session units.

import { decode, encode } from '@msgpack/msgpack';
import WebSocket from 'ws';

const ws = new WebSocket('wss://realtime.fishaudio.org/v1/tts/live', 'realtime.tts.msgpack.v1', {
  headers: { Authorization: `Bearer ${process.env.FISHAUDIO_API_KEY}` },
});

ws.on('message', (raw) => {
  const message = decode(raw);
  if (message.event === 'authenticated') {
    ws.send(encode({ event: 'start', request: { model_id: 'PUBLIC_VOICE_UUID', format: 'mp3' } }));
  } else if (message.event === 'ready') {
    ws.send(encode({ event: 'text', text: 'Hello from Realtime TTS v1.' }));
    ws.send(encode({ event: 'flush' }));
    ws.send(encode({ event: 'stop' }));
  } else if (message.event === 'audio') {
    // Append message.data to the output stream.
  } else if (message.event === 'error') {
    console.error(message.code, message.message, message.retryable);
  }
});

Billing and errors

Billing is settled per accepted flush segment. If generation fails before a segment is committed, its reservation is refunded. Do not retry blindly after receiving audio; v1 has no durable request replay or segment idempotency contract.

Protocol failures arrive as error with code, message, and retryable, followed by a WebSocket close. Common validation failures include invalid_message, empty_segment, segment_too_large, model_not_found, and insufficient_quota.

For camelCase fields, provider-neutral model switching, browser tickets, and durable recovery, migrate to Realtime Text to Speech v3.