T
Telephony SOPKnowledge Base
Search
← All topics

Fish Audio TTS — Voice Cloning and the s1 Model

runnable

Fish Audio's TTS API and voice-cloning model. Covers /v1/tts request shape (text, reference_id, prosody, latency), the Merlino voice clone (model id 3782f7b...), built-in voice presets, /model voice cloning, pricing ($15 / 1M UTF-8 bytes), and Fish vs ElevenLabs trade-offs.

fish-audiottsvoice-cloningmerlino-voiceaudio
Agent trigger phrases: Fish Audio TTS · Merlino voice · voice clone fish audio · fish.audio API · s1 model TTS · Fish Audio vs ElevenLabs · reference_id voice

Fish Audio TTS

Fish Audio is a TTS provider with strong voice cloning, low cost (~70% cheaper than ElevenLabs), and competitive quality (ranked #1 on TTS-Arena 2026). Used for cloned voiceovers, ad scripts, and post-call audio outputs.

Auth

Authorization: Bearer ${FISH_AUDIO_API_KEY}
Base URL:      https://api.fish.audio

Quick curl (with JSON file to avoid shell-escape issues)

cat > /tmp/fish-tts.json << 'ENDJSON'
{
  "text": "Hello from the Merlino voice clone.",
  "reference_id": "3782f7b36e5d411fb9d70d1809b134a0",
  "format": "mp3",
  "mp3_bitrate": 192,
  "temperature": 0.9,
  "latency": "normal",
  "chunk_length": 300,
  "prosody": { "speed": 0.9 }
}
ENDJSON

curl -X POST https://api.fish.audio/v1/tts \
  -H "Authorization: Bearer $FISH_AUDIO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "model: s1" \
  -d @/tmp/fish-tts.json \
  --output ./output.mp3

Always use a temp JSON file — text with em dashes, exclamation marks, or smart quotes breaks naive shell quoting.

TTS endpoint — full reference

POST https://api.fish.audio/v1/tts

Headers

| Header | Value | |---|---| | Authorization | Bearer YOUR_KEY | | Content-Type | application/json | | model | s1 (or speech-1.6, speech-1.5) |

Request body parameters

| Param | Type | Default | Range | Notes | |---|---|---|---|---| | text | string | required | — | Text to speak | | reference_id | string | null | — | Voice model ID | | format | string | mp3 | wav, pcm, mp3, opus | Output format | | temperature | float | 0.7 | 0-1 | Higher = more expressive | | top_p | float | 0.7 | 0-1 | Nucleus sampling | | latency | string | normal | low, balanced, normal | normal = best quality | | chunk_length | int | 300 | 100-300 | Text segment size | | mp3_bitrate | int | 128 | 64, 128, 192 | kbps (MP3 only) | | opus_bitrate | int | -1000 | -1000, 24, 32, 48, 64 | bps (opus only) | | sample_rate | int | 44100 | 8k-48k | Hz | | normalize | bool | true | — | Normalize numbers in text | | max_new_tokens | int | 1024 | — | Max audio tokens per chunk | | repetition_penalty | float | 1.2 | — | >1.0 reduces repetition | | min_chunk_length | int | 50 | 0-100 | Min chars before chunking | | prosody.speed | float | 1.0 | 0.5-2.0 | Speaking rate | | prosody.volume | float | 0 | — | dB adjustment |

Response

  • 200 — chunked audio (Transfer-Encoding: chunked)
  • 401 — bad auth
  • 402 — billing issue
  • 422 — validation error

Merlino voice (Mike's clone) — defaults

{
  "reference_id": "3782f7b36e5d411fb9d70d1809b134a0",
  "temperature": 0.9,
  "prosody": { "speed": 0.9 },
  "mp3_bitrate": 192,
  "latency": "normal",
  "chunk_length": 300
}

Built-in voice presets

| Voice | Reference ID | Vibe | |---|---|---| | Adam (popular) | 722523118ee94709b661502c76b016b7 | Young, energetic, friendly | | TikTok Adam | b8d3d639d6414cc39c964955f77fce82 | Deep, authoritative, dramatic | | E-girl | 8ef4a238714b45718ce04243307c57a7 | Female, young, casual | | Energetic Male | 802e3bc2b27e49c2995d23ef70e6ac89 | Male, high energy | | Sarah | 933563129e564b19a115bedd57b7406a | Female, clear | | Adrian | bf322df2096a46f18c579d0baa36f41d | Male, professional | | Selene | b347db033a6549378b48d00acb0d06cd | Female, smooth | | Ethan | 536d3a5e000945adb7038665781a4aca | Male, neutral |

Speed/energy guide

| Use | Speed | Temperature | |---|---|---| | Chill narration | 0.8 | 0.7 | | Merlino default | 0.9 | 0.9 | | High energy | 1.0 | 0.9 | | Fast-paced (reels) | 1.2 | 0.8 |

Voice cloning — create a new voice

POST https://api.fish.audio/model

Content-Type: multipart/form-data

| Field | Required | Notes | |---|---|---| | type | Yes | tts | | title | Yes | Model name | | train_mode | Yes | fast (instant) | | voices | Yes | Audio file(s), 10-30s clear speech recommended | | visibility | No | private, public, unlist | | texts | No | Transcript of the audio (auto via ASR if omitted) | | enhance_audio_quality | No | Boolean |

Returns a model ID — use it as reference_id in TTS calls.

Pricing

| Service | Cost | |---|---| | TTS | $15 per 1M UTF-8 bytes (~180k words / ~12 hours speech) | | ASR | $0.36 per audio hour | | Voice model | Pay-as-you-go, no subscription |

Rate limits: 5 concurrent (starter) → 15 concurrent (after $100+ spent) → custom (enterprise).

SDKs

pip install fishaudio
npm install fish-audio

Both SDKs support WebSocket streaming for low-latency real-time TTS.

Fish Audio vs ElevenLabs

| | Fish Audio | ElevenLabs | |---|---|---| | Cost/char | $0.00004 | $0.00014 | | Cost/min | ~$0.05 | ~$0.18 | | Price diff | 70% cheaper | — | | Quality rank | #1 TTS-Arena (2026) | #2-3 | | Clone min audio | 10 seconds | Few minutes | | Languages | 30+ | 70+ | | Smoothness | Good, some chunk artifacts at boundaries | Buttery smooth | | Streaming latency | Sub-500ms | Not specified | | Extras | Open-source model | Dubbing, SFX, voice isolator |

Rule of thumb: Fish Audio when cost + speed dominate. ElevenLabs when output smoothness matters more.

Using inside SignalWire flows

SignalWire's native TTS catalog (Rime, ElevenLabs, Deepgram, Cartesia) handles in-call TTS. Fish Audio is generally used for pre-rendered audio — pre-generate MP3s, host them, then play via SWML play:

- play:
    url: "https://your.cdn/clips/intro.mp3"

For dynamic per-call cloned-voice output, render to a temp URL with Fish, hand the URL to play. Adds a generation step per call.

Anti-patterns

  • Inline curl with shell-escaped JSON — punctuation breaks everywhere. Always use a temp file.
  • Using latency: low for narration — sacrifices quality. Reserve for real-time.
  • Cloning from noisy or compressed audio — Fish's enhance_audio_quality helps a little, but garbage in = garbage out.
  • Treating Fish-rendered MP3s as a substitute for in-call dynamic TTS — adds latency. For real-time conversational AI, use a SignalWire-native voice.

See also