Long text-to-speech jobs are easy to prototype, but they can become awkward in production when your app has to hold a request open while audio is being generated.
What you can do
The Fish TTS endpoint on Ace Data Cloud gives you a simple way to turn text into audio while keeping the request shape compatible with the Fish Audio text-to-speech API. In practice, that means you can send a familiar JSON body, authenticate with an Ace Data Cloud bearer token, and receive an audio_url that your app can play, download, or pass to the next step in a workflow.
The base request is intentionally small:
-
Base URL / endpoint:
POST https://api.acedata.cloud/fish/tts -
Authentication:
Authorization: Bearer {token} -
Content type:
application/json -
Model header:
model: s2-proormodel: s1(s2-prois the default) -
Required body field:
text -
Optional voice field:
reference_id -
Optional output fields:
format,sample_rate,mp3_bitrate -
Async extension:
callback_url -
Typical result field:
audio_url
This makes the endpoint useful for product narration, voice notes, generated learning content, internal tools, or any workflow where the output should become a real audio file instead of just a browser-side demo.
How it works
The synchronous version is the smallest useful call. Send text, include your token, choose the model in the request header, and read the returned audio_url:
curl -X POST 'https://api.acedata.cloud/fish/tts' \
-H 'accept: application/json' \
-H 'authorization: Bearer {token}' \
-H 'content-type: application/json' \
-H 'model: s2-pro' \
-d '{
"text": "今天天气真好,我们一起出去散散步吧。"
}'
A successful response returns an audio file URL:
{
"audio_url": "https://platform.r2.fish.audio/task/8a72ff9840234006a9f74cb2fa04f978.mp3"
}
If your app is already written against Fish Audio's official request body, the important migration detail is that the body structure stays the same. The main change is authentication: use Authorization: Bearer {token} with the token from Ace Data Cloud. The endpoint also accepts Fish request fields such as text, reference_id, references, prosody, format, sample_rate, mp3_bitrate, chunk_length, temperature, and top_p.
Add a cloned voice or explicit audio format
For many real apps, plain TTS is only the first step. You may want a known voice, a predictable file type, or a fixed sample rate for a downstream media pipeline. The endpoint supports reference_id, format, and sample_rate in the JSON body:
curl -X POST 'https://api.acedata.cloud/fish/tts' \
-H 'accept: application/json' \
-H 'authorization: Bearer {token}' \
-H 'content-type: application/json' \
-H 'model: s2-pro' \
-d '{
"text": "今天天气真好,我们一起出去散散步吧。",
"reference_id": "d7900c21663f485ab63ebdb7e5905036",
"format": "mp3",
"sample_rate": 44100
}'
That is the shape I would use when integrating TTS into a service that stores generated audio assets. Your backend can submit the text, save the resulting audio_url, and associate it with the original content record.
Use callbacks for longer jobs
For short snippets, a direct request is fine. For longer text, keeping an HTTP connection open can make the rest of your system more fragile: workers time out, clients retry, and duplicate jobs become harder to reason about.
Ace Data Cloud adds an async callback extension through callback_url. When you include this field, the API immediately returns a task_id and started_at timestamp. Later, when generation completes, the final payload is posted to your callback URL with the same task_id and the generated audio_url.
curl -X POST 'https://api.acedata.cloud/fish/tts' \
-H 'accept: application/json' \
-H 'authorization: Bearer {token}' \
-H 'content-type: application/json' \
-H 'model: s2-pro' \
-d '{
"text": "今天天气真好,我们一起出去散散步吧。",
"callback_url": "https://webhook.site/4815f79f-a40f-4078-ac85-1cc126b6bb34"
}'
Immediate response:
{
"task_id": "2725a2d3-f87e-4905-9c53-9988d5a7b2f5",
"started_at": "2025-05-09T12:34:56.789Z"
}
Callback payload:
{
"task_id": "2725a2d3-f87e-4905-9c53-9988d5a7b2f5",
"audio_url": "https://platform.r2.fish.audio/task/b627c2f7d38a4083a837570ba6d0962f.mp3"
}
In a production app, store the task_id as soon as the first response arrives. When your webhook receives the callback, look up the pending job, save the audio_url, and mark the job complete. If you need active status checks instead, the documentation notes that the task_id can also be used with the Fish Tasks API.
Handle errors as part of the workflow
Do not treat TTS as a fire-and-forget side effect. The endpoint preserves upstream HTTP status behavior and returns a unified platform error format. The common cases to handle are 400 token_mismatched, 400 api_not_implemented, 401 invalid_token, 429 too_many_requests, and 500 api_error.
A typical error response looks like this:
{
"success": false,
"error": {
"code": "api_error",
"message": "fetch failed"
},
"trace_id": "2cf86e86-22a4-46e1-ac2f-032c0f2a4e89"
}
Keep the trace_id in logs. It is the kind of small detail that saves time when debugging a failed media generation job later.
A practical integration pattern
For a builder-friendly implementation, I would start with three states in my own database: queued, processing, and ready. The app creates a row when a user submits text, calls POST /fish/tts with callback_url, stores the returned task_id, and moves the row to processing. The webhook validates the payload, saves audio_url, and marks the row as ready.
This keeps your UI responsive and avoids making users wait on a long request. It also gives you a clean retry boundary: retry the job submission if the initial request fails, and retry webhook processing if your own server has a temporary issue.
If you want the exact compatibility notes and field list, read the full Fish TTS integration guide on Ace Data Cloud: https://platform.acedata.cloud/documents/fish-tts-integration
Top comments (0)