Fish TTS API Integration Instructions

This interface provides text-to-speech, calls to saved voices, and one-time instant voice cloning, with the address POST https://api.ai.moses.day/fish/tts.

Application Process

To use the Fish TTS API, first obtain your API Token from the Mai Console for future reference.

If you are not logged in or registered, you will be automatically redirected to the login page to invite you to register and log in, and will return to the current page upon completion.

One API Token can call all services on the platform, no need to apply separately for each service. The first application will grant a free quota for a free experience; when the quota is insufficient, you can recharge the general balance in the console.

📘 Complete Documentation: Fish TTS API →

Request Headers

Header Required Description
authorization Yes Bearer {token}, where {token} is the key applied for on this platform.
content-type Yes application/json.
accept No application/json.
model No TTS model, optional s1, s2-pro, or s2.1-pro, default is s2-pro. s2.1-pro is the latest generation, s2-pro is expressive; s1 is more stable and less likely to deviate with long texts. All three are priced the same.

Request Body Fields

Field Type Required Description
text string Yes The text to be synthesized, a non-empty string.
format string No Output audio format, optional mp3 (default), wav, pcm. Both wav and pcm return a WAV container. opus is not supported and will return 400 if provided.
reference_id string | string[] No Model ID of saved or public voices, can be created by Fish Model API, or retrieved in Fish Model Query. Cannot be used with references simultaneously.
references object[] No One-time instant voice cloning, only supports one {audio, text} sample: audio is a public HTTPS MP3/WAV URL, text is the accurate transcript of the audio. Cannot be used with reference_id simultaneously.
sample_rate integer No Sample rate, commonly 16000, 22050, 44100. Default is 44100 for format=mp3.
mp3_bitrate integer No MP3 bitrate, optional 64, 128, 192. Only effective for format=mp3.
prosody object No Prosody overrides, supports speed (speech rate, 1.0 is normal speed) and volume (volume gain dB). For example, {"speed":1.2,"volume":0}.
chunk_length integer No Upstream chunk length, default determined by upstream.
temperature number No Sampling temperature, range approximately 0.0–1.0.
top_p number No Top-p sampling parameter.
latency string No normal or balanced, defaults to normal automatically filled by this interface (upstream will reject if an empty string is passed).
normalize boolean No Whether to normalize the text.
callback_url string No Asynchronous callback address, see below "Asynchronous Callback". This is an extension relative to the official interface.

One-time cloning only accepts HTTPS audio URLs, does not accept MessagePack, Base64, data URI, or credentialed URLs. Recommended reference audio length is 10–270 seconds; Studio uses a more conservative range of 10–60 seconds.

Example 1: Minimal Request (text + format=mp3)

curl -X POST 'https://api.ai.moses.day/fish/tts' \
  -H 'authorization: Bearer {token}' \
  -H 'content-type: application/json' \
  -d '{
    "text": "Hello world.",
    "format": "mp3"
  }'

Response (actual test):

{
  "audio_url": "https://platform2.cdn.acedata.cloud/fish/e2ffcc06-18da-4a8c-b9aa-9337d0f9ec1d.mp3"
}

audio_url points to the CDN of this platform, which can be directly downloaded via GET or played in <audio>. The final successful response will also return the top-level cost, where amount is the actual Credits deducted for this transaction; if there is an account discount, list_amount indicates the amount before the discount. The link is long-term available, but it is still recommended to keep a copy in your own storage.

Example 2: Using Cloned Voice reference_id

Below is a public Spanish voice on the Fish platform (the _id can be retrieved through Fish Model Query):

curl -X POST 'https://api.ai.moses.day/fish/tts' \
  -H 'authorization: Bearer {token}' \
  -H 'content-type: application/json' \
  -d '{
    "text": "Hermanos míos, hoy es un buen día.",
    "reference_id": "8d2c17a9b26d4d83888ea67a1ee565b2",
    "format": "mp3"
  }'

Response (actual test):

{
  "audio_url": "https://platform2.cdn.acedata.cloud/fish/b6f161f2-a100-4818-add2-47694f234659.mp3"
}

Example 3: One-time Instant Voice Cloning (references)

Place the reference audio at a public HTTPS address and provide the exact original text spoken. This voice is only used for this synthesis and will not create a long-term model:

curl -X POST 'https://api.ai.moses.day/fish/tts' \
  -H 'authorization: Bearer {token}' \
  -H 'content-type: application/json' \
  -H 'model: s2-pro' \
  -d '{
    "text": "新的旅程从这一刻开始,让我们一起向前。",
    "format": "mp3",
    "references": [{
      "audio": "https://platform2.cdn.acedata.cloud/fish/6220d605-39d0-4d43-9e58-0f12949dc9b9.mp3",
      "text": "春天的清晨,阳光穿过树叶,落在安静的小路上。"
    }]
  }'

Actual successful response:

{
  "audio_url": "https://platform2.cdn.acedata.cloud/fish/995dfe37-b187-474d-8323-b08d6678ed8f.mp3",
  "cost": {"amount": 0.007609319999999999, "currency": "credit"}
}
Method Lifecycle Applicable Scenarios
references Only for this TTS request Temporary use or different voice each time
reference_id Reusable Public voices or long-term voices created via /fish/model

Example 3: Adjusting Speech Rate / Volume (prosody)

curl -X POST 'https://api.ai.moses.day/fish/tts' \
  -H 'authorization: Bearer {token}' \
  -H 'content-type: application/json' \
  -d '{
    "text": "Faster speech with prosody overrides.",
    "prosody": { "speed": 1.2, "volume": 0 },
    "format": "mp3"
  }'

Response (actual test):

{
  "audio_url": "https://platform2.cdn.acedata.cloud/fish/5ade0339-5f11-487e-aacc-06a908271706.mp3"
}

speed greater than 1 speeds up, less than 1 slows down; volume is in dB, 0 means no change, positive numbers increase, negative numbers decrease.

Example 5: Switching Model + Controlling Bitrate

Switch to stable model via HTTP header model: s1, add mp3_bitrate: 128 in the request body to control MP3 bitrate:

curl -X POST 'https://api.ai.moses.day/fish/tts' \
  -H 'authorization: Bearer {token}' \
  -H 'content-type: application/json' \
  -H 'model: s1' \
  -d '{
    "text": "high bitrate mp3",
    "format": "mp3",
    "mp3_bitrate": 128
  }'

Response (actual test):

{
  "audio_url": "https://platform2.cdn.acedata.cloud/fish/7e7abf3d-3d72-4c9f-8eb6-8af932d7c96e.mp3"
}

Example 6: PCM Raw Waveform

Recommended for scenarios where real-time stitching in the browser or subsequent processing (mixing, speed change) is needed, use pcm:

curl -X POST 'https://api.ai.moses.day/fish/tts' \
  -H 'authorization: Bearer {token}' \
  -H 'content-type: application/json' \
  -d '{
    "text": "hi",
    "format": "pcm",
    "sample_rate": 16000
  }'

Response (actual test):

{
  "audio_url": "https://platform2.cdn.acedata.cloud/fish/64adc04b-c196-4a0f-9070-222ba101ce6c.wav"
}

The extension of the link follows the format in the request: .mp3 for mp3, .wav for wav and pcm (WAV container, 16 bit PCM).

Asynchronous Callback (callback_url)

Long text synthesis may take several seconds to tens of seconds, and if the connection is interrupted, it needs to be retried. After passing callback_url in the request body, the interface will immediately return {task_id, started_at}, and when the upstream completes, it will POST the complete result back to that URL in JSON format, with the same task_id and the top-level cost for this billing. The initial task confirmation has not completed synthesis, so it does not include cost.

curl -X POST 'https://api.ai.moses.day/fish/tts' \
  -H 'authorization: Bearer {token}' \
  -H 'content-type: application/json' \
  -d '{
    "text": "The weather is really nice today, let's go for a walk together.",
    "format": "mp3",
    "callback_url": "https://webhook.site/4815f79f-a40f-4078-ac85-1cc126b6bb34"
  }'

Immediate response (actual test):

{
  "task_id": "79d82713-2897-4eeb-9934-e7544d471aa7",
  "started_at": 1778462584.742
}

Later, callback_url will receive something like:

{
  "task_id": "79d82713-2897-4eeb-9934-e7544d471aa7",
  "audio_url": "https://platform2.cdn.acedata.cloud/fish/bd66b8c5-7543-4557-b684-baa72407e336.mp3"
}

You can also actively pull results by task_id using Fish Tasks API; the final state record's response.cost is consistent with the cost in the callback, see that document for details.

Error Handling

  • 400 token_mismatched: Missing or invalid request parameters (most common is text being empty, or format having a value other than mp3/wav/pcm).
  • 401 invalid_token: Authentication token does not exist or is invalid.
  • 429 too_many_requests: Account rate limit triggered.
  • 500 api_error: Internal server error.

Example of error response:

{
  "success": false,
  "error": {
    "code": "api_error",
    "message": "fetch failed"
  },
  "trace_id": "2cf86e86-22a4-46e1-ac2f-032c0f2a4e89"
}

Parameter validation errors will be explained in the message field, indicating the invalid fields, for example:

{
  "status": 400,
  "message": "[{\"type\":\"literal_error\",\"loc\":[\"format\"],\"msg\":\"Input should be 'pcm' or 'mp3'\",\"input\":\"wav\"}]"
}

Conclusion

The minimum cost to integrate Fish TTS is: replace the authentication in the existing code calling api.fish.audio/v1/tts with the platform token, and explicitly include format: "mp3" in the request body. For long text scenarios, it is recommended to use callback_url for asynchronous callbacks; for discovering cloned voice reference_id, please refer to Fish Model Query and Fish Model Get.