Fish Audio S2.1 Pro API: Pricing, Documentation

by Fish Audio

Fish Audio API provides a powerful, high-performance text-to-speech platform designed for rapid voice synthesis and cloning. It features advanced artificial intelligence models that process written text into highly natural, human-like voice outputs with extremely low latency. The interface enables developers to easily integrate professional audio capabilities, customize tonal qualities, and generate high-fidelity audio streams for diverse applications.

Get API Key
Fish Audio API

Models Version

Fish Audio Drama 3 API Documentation

Direct a voice performance in plain language: put directions in square brackets, place sounds and pauses where they happen, change the delivery of specific words with paired tags, and render a whole multi-speaker scene in one request, in 83 languages. Asynchronous: submit returns a request_id; poll the status endpoint until the request is COMPLETED.

POST https://gateway.pixazo.ai/fish-audio-drama-3/v1/text-to-speech

Preview. Results vary between takes and Fish Audio may update the model during the preview; each take is a separate billed request.

Authentication

All requests require an API key passed via header.

HeaderTypeRequiredDescription
Ocp-Apim-Subscription-KeystringYesYour API subscription key

Text to Speech - Fish Audio Drama 3

Request Code

POST https://gateway.pixazo.ai/fish-audio-drama-3/v1/text-to-speech
Content-Type: application/json
Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY

{
  "text": "[tired, trying to sound upbeat] I told you this would happen. [sigh] <whisper>But I am glad you came back.</whisper>",
  "reference_id": "f3a2b90078d54a65af4b95f63bc7798e",
  "format": "mp3"
}
import requests

url = "https://gateway.pixazo.ai/fish-audio-drama-3/v1/text-to-speech"
headers = {
    "Content-Type": "application/json",
    "Ocp-Apim-Subscription-Key": "YOUR_SUBSCRIPTION_KEY"
}
data = {
  "text": "[tired, trying to sound upbeat] I told you this would happen. [sigh] <whisper>But I am glad you came back.</whisper>",
  "reference_id": "f3a2b90078d54a65af4b95f63bc7798e",
  "format": "mp3"
}

resp = requests.post(url, json=data, headers=headers)
print(resp.json())
const res = await fetch("https://gateway.pixazo.ai/fish-audio-drama-3/v1/text-to-speech", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Ocp-Apim-Subscription-Key": "YOUR_SUBSCRIPTION_KEY"
  },
  body: JSON.stringify({
  "text": "[tired, trying to sound upbeat] I told you this would happen. [sigh] <whisper>But I am glad you came back.</whisper>",
  "reference_id": "f3a2b90078d54a65af4b95f63bc7798e",
  "format": "mp3"
})
});
console.log(await res.json());
curl -X POST 'https://gateway.pixazo.ai/fish-audio-drama-3/v1/text-to-speech' \
  -H 'Content-Type: application/json' \
  -H 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY' \
  --data-raw '{"text": "[tired, trying to sound upbeat] I told you this would happen. [sigh] <whisper>But I am glad you came back.</whisper>", "reference_id": "f3a2b90078d54a65af4b95f63bc7798e", "format": "mp3"}'

Output

{
  "request_id": "fish-audio-drama-3_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "QUEUED",
  "polling_url": "https://gateway.pixazo.ai/v2/requests/status/fish-audio-drama-3_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

Webhook (Optional)

Instead of polling, you can receive a Webhook callback when the request reaches a terminal state. Provide a Webhook URL via header on the submit request.

HeaderRequiredDescription
X-Webhook-URLTo enableHTTPS URL to receive the Webhook callback.
X-Webhook-ModeNoterminal (default, one callback on COMPLETED/ERROR) or sync (per-poll callbacks).

Example: enable Webhook

curl -X POST 'https://gateway.pixazo.ai/fish-audio-drama-3/v1/text-to-speech' \
  -H 'Content-Type: application/json' \
  -H 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY' \
  -H 'X-Webhook-URL: https://your-server.com/webhook' \
  --data-raw '{"text": "[tired, trying to sound upbeat] I told you this would happen. [sigh] <whisper>But I am glad you came back.</whisper>", "reference_id": "f3a2b90078d54a65af4b95f63bc7798e", "format": "mp3"}'

Callback Payload (success)

{
  "request_id": "fish-audio-drama-3_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "COMPLETED",
  "model_id": "fish-audio-drama-3",
  "error": null,
  "output": {
    "media_url": [
      "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/v1/fish-audio-drama-3_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/output.mp3"
    ],
    "media_type": "audio/mpeg"
  },
  "created_at": "2026-10-14T16:40:58.264Z",
  "updated_at": "2026-10-14 16:42:31",
  "completed_at": "2026-10-14 16:42:31"
}

Failure callback shape

{
  "request_id": "fish-audio-drama-3_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "ERROR",
  "model_id": "fish-audio-drama-3",
  "error": "Description of the failure",
  "output": null,
  "created_at": "2026-10-14T16:40:58.264Z",
  "updated_at": "2026-10-14 16:42:31",
  "completed_at": "2026-10-14 16:42:31"
}

Delivery semantics

  • terminal mode: one Webhook callback when the request is COMPLETED or ERROR.
  • sync mode: a Webhook callback on each status change.
  • Callbacks are idempotent on request_id — de-duplicate on it.
  • Respond 200 within a few seconds; the Webhook endpoint must be HTTPS.

Request Parameters

ParameterRequiredTypeDefaultAllowed values / rangeDescription
textYesstring—1 – 5000 charactersThe script to perform. Directions in square brackets, paired tags and speaker tags go inline in this string (see Directions and tags below); every one of them counts toward the 5,000-character limit and toward the billed size.
reference_idNostring or array—32-char hex voice id, or an array of up to 8The voice(s) to perform with. Pass a single string for one speaker, or an array of ids for multi-speaker dialogue (see Multi-Speaker Dialogue below). Omit to use the provider default voice. Browse voices at fish.audio; the id is the last path segment of fish.audio/m/<id>. You are responsible for holding the rights to any voice you reference.
formatNostringmp3mp3, wavOutput audio container. mp3 returns audio/mpeg; wav returns 16-bit mono PCM at 44.1 kHz.
mp3_bitrateNointeger12864, 128, 192MP3 bitrate in kbps. Only valid when format is mp3; sent together with wav, the request fails.
latencyNostringbalancednormal, balancedSynthesis latency trade-off. balanced (our default) favours a shorter time to first audio; normal accepts more latency in exchange for the provider's best quality.
chunk_lengthNointeger300100 to 300Text chunk size used during synthesis. Smaller chunks start faster; larger chunks give steadier prosody across long passages.
normalizeNobooleantruetrue, falseNormalize numbers, dates and abbreviations in English and Chinese text before synthesis.
temperatureNonumber—0.0 to 1.0Controls expressiveness: higher is more varied, lower is more consistent. Omit it to use the model's own default.
top_pNonumber—0.0 to 1.0Controls diversity via nucleus sampling. Omit it to use the model's own default.
prosodyNoobject——Optional prosody controls (see the two fields below).
prosody.speedNonumber1.00.5 to 2.0Speaking rate multiplier. 2.0 is twice as fast, 0.5 is half speed.
prosody.volumeNonumber0-20 to 20Volume adjustment in decibels.

How text is measured

Billed on the UTF-8 bytes of text, including every direction, tag and speaker marker, rounded up to whole 1,000-byte blocks (minimum one). Most non-Latin scripts use 2–4 bytes per character. The rate is in the pricing panel on this page.

Billing is measured in UTF-8 bytes, not characters. For English and other Latin-script text the two are the same — one character is one byte — but characters outside the ASCII range take more than one byte. Directions and tags are text too: [sigh] adds 6 bytes and <|speaker:0|> adds 13.

InputCharactersUTF-8 bytesBilled blocks
English5005001
English1,2001,2002
Chinese / Japanese (3 bytes per character)4161,2482
Longest single request, English5,0005,0005
Longest single request, Chinese / Japanese5,00015,00015

The 5,000-character limit on text counts characters, not bytes — and characters outside the Basic Multilingual Plane (most emoji) count as two toward it.

Parameters not supported

Worth reading if you are porting existing Fish Audio code — some parameters are rejected outright, some fail once the request is processed, and others are accepted and then ignored.

Rejected with 400

Checked when you submit: the request is rejected before it is queued.

ParameterWhyUse instead
modelThe model is fixed for this endpoint (Fish Audio Drama 3) and is not caller-selectable.—
referencesInline zero-shot voice cloning requires a MessagePack request body, which this JSON endpoint cannot carry.reference_id with a Fish Audio voice id
format: "pcm" / "opus"Not offered; mp3 and wav cover playback in every browser and player.mp3 or wav
n, batch_size, count, num_images, number_of_imagesThis API returns one audio file per request.One request per audio file (every take is its own request)

Accepted, then the request fails

These values pass the submit check, so the submit returns 202, but they are validated while the request is processed: the request fails, and the status endpoint (or Webhook) reports status: "ERROR" with an error message that names the problem.

ParameterWhen the request fails
reference_idNot a 32-character hexadecimal voice id; an empty array; more than 8 ids; two or more ids without any <|speaker:N|> tag in text; an id that does not name an existing voice.
mp3_bitrateSent together with format: "wav".
prosodyNot an object; prosody.speed outside 0.5 to 2.0; prosody.volume outside -20 to 20.

Accepted but ignored

These are silently dropped — the request still returns 202, but the value has no effect on the generated audio. Only the parameters listed in the table above are forwarded.

sample_rate, opus_bitrate, max_new_tokens, repetition_penalty, min_chunk_length, condition_on_previous_chunks, early_stop_threshold, features, pronunciation_dictionary, prosody.normalize_loudness, and any parameter not listed above

Voice, Directions & Dialogue

Choosing a voice

reference_id accepts two forms. Omit it entirely to use the provider default voice. A voice that fits the role makes the biggest difference to how well a performance lands.

FormUse forValue
StringA single speaker"f3a2b90078d54a65af4b95f63bc7798e"
ArrayMulti-speaker dialogue["<speaker-0-id>", "<speaker-1-id>"] — up to 8 ids

Voice ids are 32-character hexadecimal strings. Browse the Fish Audio voice library at fish.audio — the id is the last path segment of fish.audio/m/<id>. Three public voices to start with:

VoiceLanguagereference_id
SheilaEnglish (US), femalef3a2b90078d54a65af4b95f63bc7798e
DeliaEnglish (US), femalec70cfb98867a4bfb9ae20d2401257b92
AdrianEnglish (US), malebf322df2096a46f18c579d0baa36f41d

Directions and tags

Directions are plain text inside text, and you can mix them freely in the same line. Bracketed directions are performance notes, not words to speak. There is no fixed list: describe tone, pacing and character in your own words.

TechniqueHow to write itExample
Describe the deliveryA direction in square brackets before the words it applies to. Write it the way you would brief a voice actor: emotion, intent, pacing, or who the character is talking to. A new direction mid-line changes the read from that point.[warm, a little nervous, speaking to an old friend] It has been a long time. [suddenly serious] We need to talk.
Sounds and pausesPut the sound or pause in brackets exactly where it should happen: a sigh, a laugh, a breath, a short pause.I did it. [sigh] I finally did it. [small laugh] [pause] Nobody believed me.
Directions in any languageDirections can be written in English or in another language, and they do not have to match the language of the script.[con voz suave y cansada] I will be home late tonight.
Change only specific wordsWrap the words in a paired tag to change the delivery of those words only: <whisper>, <emphasis>, <soft>, <fast>, <slow>, <stress>, <forceful>.Listen carefully. <whisper>They are right behind us.</whisper> <fast>Run, now!</fast>
Switch language mid-line (Experimental)Wrap a phrase in a language tag such as <french>, <spanish>, <german>, <japanese>, <chinese>, <korean> or <italian> to switch language for those words only.My grandmother always said <spanish>poco a poco</spanish>, which means little by little.
Pin a pronunciationWrap the phonemes in <|phoneme_start|> and <|phoneme_end|>: CMU Arpabet for English (the digit on a vowel marks stress: 1 primary, 2 secondary, 0 unstressed), pinyin for Chinese, romaji for Japanese.You say <|phoneme_start|>T AH0 M EY1 T OW2<|phoneme_end|>, I say <|phoneme_start|>T AH0 M AA1 T OW2<|phoneme_end|>.
  • Paired tags are written exactly as listed; in this API the speed tags are <fast> and <slow>.
  • Language tags are experimental: they are not guaranteed for every language or voice, and the model may switch language on its own when the text is already multilingual. Wrap only the words that change language; a voice whose speaker is fluent in both languages works best.
  • Directions, tags and speaker markers count toward the 5,000-character limit and toward the billed size (see How text is measured above).

Multi-Speaker Dialogue

Pass an array of voice ids and start each turn in text with a <|speaker:N|> tag. The index in the tag selects the voice at that position in the array — <|speaker:0|> uses the first id, <|speaker:1|> the second, and so on. Directions and paired tags work inside each turn. This scene uses two voices:

{
  "text": "<|speaker:0|>[calm, measured storyteller voice] The ship had been silent for three days.<|speaker:1|>[low, gravelly, tired but in control] Status report.<|speaker:0|>[barely above a whisper] Nobody answered.<|speaker:1|>[slow, dangerously quiet] Then we wait. <emphasis>Together.</emphasis>",
  "reference_id": [
    "c70cfb98867a4bfb9ae20d2401257b92",
    "bf322df2096a46f18c579d0baa36f41d"
  ],
  "format": "mp3"
}
  • Text before any tag is spoken by the first voice in the array.
  • Supply one id per speaker index you use, up to 8 ids.
  • With two or more ids and no <|speaker:N|> tag in text, the request fails, because every line would otherwise be rendered in the first voice.
  • For a single speaker, pass the id as a plain string — tags are not needed.

Example Request

{
  "text": "[tired, trying to sound upbeat] I told you this would happen. [sigh] <whisper>But I am glad you came back.</whisper>",
  "reference_id": "f3a2b90078d54a65af4b95f63bc7798e",
  "format": "mp3"
}

Example Response

{
  "request_id": "fish-audio-drama-3_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "QUEUED",
  "polling_url": "https://gateway.pixazo.ai/v2/requests/status/fish-audio-drama-3_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

Request Headers

HeaderRequiredDescription
Content-TypeYesapplication/json
Ocp-Apim-Subscription-KeyYesYour API subscription key.
X-Webhook-URLNoEnable Webhook callbacks (see Webhook section).

Response Handling

Status CodeMeaning
202Accepted — request queued; returns request_id and polling_url.
400Bad request — the body failed the submit check and was not queued: text missing, blank or over 5000 characters; a format other than mp3 or wav; mp3_bitrate, latency or chunk_length outside its allowed values; temperature or top_p not a number from 0 to 1; normalize not a boolean; or any of model, references, n, batch_size, count, num_images, number_of_images (see “Parameters not supported” above).
401Unauthorized — missing or invalid subscription key.
402Insufficient balance.
403Forbidden.
429Too many requests.
500Internal server error.

Problems that can only be detected while the request is processed (“Accepted, then the request fails” above) are not a 400: they are reported through the status endpoint or Webhook with status: "ERROR".

Error Responses

A 400 names every rejected parameter in violations:

{
  "error": "Invalid request parameters",
  "message": "'format' must be one of: \"mp3\", \"wav\" (got \"opus\")",
  "violations": [
    "'format' must be one of: \"mp3\", \"wav\" (got \"opus\")"
  ]
}

A request that fails after it was queued is reported via the status endpoint (or Webhook) with status: "ERROR" and an error message — for example when reference_id does not name an existing voice.

Retrieving Results

Poll the status endpoint with the request_id from the submit response until status is COMPLETED or ERROR, then download the audio from output.media_url.

curl 'https://gateway.pixazo.ai/v2/requests/status/fish-audio-drama-3_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' \
  -H 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY'

Completed response

Captured from a real Drama 3 request at launch; the request id is masked.

{
  "request_id": "fish-audio-drama-3_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "COMPLETED",
  "model_id": "fish-audio-drama-3",
  "error": null,
  "output": {
    "media_url": [
      "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/v1/fish-audio-drama-3_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/output.mp3"
    ],
    "media_type": "audio/mpeg"
  },
  "created_at": "2026-10-14T16:40:58.264Z",
  "updated_at": "2026-10-14 16:42:31",
  "completed_at": "2026-10-14 16:42:31"
}

Response Fields

FieldTypeDescription
request_idstringUnique request identifier.
statusstringQUEUED, PROCESSING, COMPLETED or ERROR.
model_idstringThe model that handled the request.
errorstring or nullError message when status is ERROR; otherwise null.
output.media_urlarray of stringsURL of the generated audio (one file per request). Download it and keep your own copy.
output.media_typestringAudio MIME type (audio/mpeg for mp3, audio/wav for wav).
created_atstringRequest creation timestamp.
updated_atstringLast status change timestamp.
completed_atstringCompletion timestamp.
polling_urlstringStatus URL for this request (in the submit response).

Status Values & Flow

QUEUED → PROCESSING → COMPLETED (success) or ERROR (failure). Treat any status other than QUEUED, PROCESSING or COMPLETED as a failure.

  • Save the request_id from the 202 response.
  • Poll the status endpoint every few seconds, or use a Webhook instead.
  • When status is COMPLETED, download the audio from output.media_url.

Fish Audio Drama 3 API Pricing

No data available

Could not load current pricing

2. Fish Audio S2.1 Pro

Fish Audio S2.1 Pro API Documentation

Generate natural, expressive speech from text in 83 languages, with free-form emotion markers and a library of over 2.5 million community voices. Asynchronous: submit returns a request_id; poll the status endpoint until the request is COMPLETED. Typical synthesis completes in a few seconds.

POST https://gateway.pixazo.ai/fish-audio-s2-1-pro/v1/text-to-speech

Authentication

All requests require an API key passed via header.

HeaderTypeRequiredDescription
Ocp-Apim-Subscription-KeystringYesYour API subscription key

Text to Speech - Fish Audio S2.1 Pro

Request Code

POST https://gateway.pixazo.ai/fish-audio-s2-1-pro/v1/text-to-speech
Content-Type: application/json
Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY

{
  "text": "[whispering] When you are building something new, there is this mix of wonder and fear. [chuckling] But that is what makes it worth building.",
  "reference_id": "933563129e564b19a115bedd57b7406a",
  "format": "mp3",
  "mp3_bitrate": 128
}
import requests

url = "https://gateway.pixazo.ai/fish-audio-s2-1-pro/v1/text-to-speech"
headers = {
    "Content-Type": "application/json",
    "Ocp-Apim-Subscription-Key": "YOUR_SUBSCRIPTION_KEY"
}
data = {
  "text": "[whispering] When you are building something new, there is this mix of wonder and fear. [chuckling] But that is what makes it worth building.",
  "reference_id": "933563129e564b19a115bedd57b7406a",
  "format": "mp3",
  "mp3_bitrate": 128
}

resp = requests.post(url, json=data, headers=headers)
print(resp.json())
const res = await fetch("https://gateway.pixazo.ai/fish-audio-s2-1-pro/v1/text-to-speech", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Ocp-Apim-Subscription-Key": "YOUR_SUBSCRIPTION_KEY"
  },
  body: JSON.stringify({
  "text": "[whispering] When you are building something new, there is this mix of wonder and fear. [chuckling] But that is what makes it worth building.",
  "reference_id": "933563129e564b19a115bedd57b7406a",
  "format": "mp3",
  "mp3_bitrate": 128
})
});
console.log(await res.json());
curl -X POST 'https://gateway.pixazo.ai/fish-audio-s2-1-pro/v1/text-to-speech' \
  -H 'Content-Type: application/json' \
  -H 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY' \
  --data-raw '{"text": "[whispering] When you are building something new, there is this mix of wonder and fear. [chuckling] But that is what makes it worth building.", "reference_id": "933563129e564b19a115bedd57b7406a", "format": "mp3", "mp3_bitrate": 128}'

Output

{
  "request_id": "fish-audio-s2-1-pro_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "QUEUED",
  "polling_url": "https://gateway.pixazo.ai/v2/requests/status/fish-audio-s2-1-pro_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

Webhook (Optional)

Instead of polling, you can receive a Webhook callback when the request reaches a terminal state. Provide a Webhook URL via header on the submit request.

HeaderRequiredDescription
X-Webhook-URLTo enableHTTPS URL to receive the Webhook callback.
X-Webhook-ModeNoterminal (default, one callback on COMPLETED/ERROR) or sync (per-poll callbacks).

Example: enable Webhook

curl -X POST 'https://gateway.pixazo.ai/fish-audio-s2-1-pro/v1/text-to-speech' \
  -H 'Content-Type: application/json' \
  -H 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY' \
  -H 'X-Webhook-URL: https://your-server.com/webhook' \
  --data-raw '{"text": "[whispering] When you are building something new, there is this mix of wonder and fear. [chuckling] But that is what makes it worth building.", "reference_id": "933563129e564b19a115bedd57b7406a", "format": "mp3", "mp3_bitrate": 128}'

Callback Payload (success)

{
  "request_id": "fish-audio-s2-1-pro_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "COMPLETED",
  "model_id": "fish-audio-s2-1-pro",
  "output": {
    "media_url": "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/fish-audio/1785407392754-66f0f615.mp3",
    "media_type": "audio/mpeg"
  },
  "created_at": "2026-07-30T10:29:44.313Z",
  "completed_at": "2026-07-30T10:29:53.391Z"
}

Failure callback shape

{
  "request_id": "fish-audio-s2-1-pro_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "ERROR",
  "model_id": "fish-audio-s2-1-pro",
  "error": "Description of the failure"
}

Delivery semantics

  • terminal mode: one Webhook callback when the request is COMPLETED or ERROR.
  • sync mode: a Webhook callback on each status change.
  • Callbacks are idempotent on request_id — de-duplicate on it.
  • Respond 200 within a few seconds; the Webhook endpoint must be HTTPS.

Request Parameters

ParameterRequiredTypeDefaultAllowed values / rangeDescription
textYesstring—1 – 5000 charactersThe text to synthesize. Supports emotion and effect markers such as [whispering], [chuckling], [laughing], [sighing] and [audience laughing]. Markers are free-form and add no cost or latency.
reference_idNostring or array—32-char hex voice id, or an array of up to 8The voice(s) to speak with. Pass a single string for one speaker, or an array of ids for multi-speaker dialogue (see Multi-Speaker Dialogue below). Omit to use the provider default voice. Browse voices at fish.audio; the id is the last path segment of fish.audio/m/<id>. You are responsible for holding the rights to any voice you reference.
formatNostringmp3mp3, wavOutput audio container. mp3 returns audio/mpeg; wav returns 16-bit mono PCM at 44.1 kHz.
mp3_bitrateNointeger12864, 128, 192MP3 bitrate in kbps. Only valid when format is mp3; sending it with wav returns 400.
latencyNostringbalancednormal, balancedSynthesis latency trade-off. balanced (our default) favours the lowest time-to-first-audio; normal accepts more latency in exchange for the provider's best quality.
chunk_lengthNointeger300100 to 300Text chunk size used during synthesis. Smaller chunks start faster; larger chunks give steadier prosody across long passages.
normalizeNobooleantruetrue, falseNormalize numbers, dates and abbreviations in English and Chinese text before synthesis.
temperatureNonumber0.70.0 to 1.0Controls expressiveness. Higher is more varied, lower is more consistent.
top_pNonumber0.70.0 to 1.0Controls diversity via nucleus sampling.
prosodyNoobject——Optional prosody controls (see the two fields below).
prosody.speedNonumber1.00.5 to 2.0Speaking rate multiplier. 2.0 is twice as fast, 0.5 is half speed.
prosody.volumeNonumber0-20 to 20Volume adjustment in decibels.

Parameters not supported

Worth reading if you are porting existing Fish Audio code — some parameters are rejected outright, while others are accepted and then ignored.

Rejected with 400

ParameterWhyUse instead
modelThe model is fixed for this endpoint and is not caller-selectable.—
referencesInline zero-shot voice cloning requires a MessagePack request body, which this JSON endpoint cannot carry.reference_id with a Fish Audio voice id
format: "pcm" / "opus"Not offered; mp3 and wav cover playback in every browser and player.mp3 or wav

Accepted but ignored

These are silently dropped — the request still returns 202, but the value has no effect on the generated audio. Only the parameters listed in the table above are forwarded.

sample_rate, opus_bitrate, max_new_tokens, repetition_penalty, min_chunk_length, condition_on_previous_chunks, early_stop_threshold, features, prosody.normalize_loudness, and any parameter not listed above

Voice & Emotion Control

Choosing a voice

reference_id accepts two forms. Omit it entirely to use the provider default voice.

FormUse forValue
StringA single speaker"933563129e564b19a115bedd57b7406a"
ArrayMulti-speaker dialogue["<speaker-0-id>", "<speaker-1-id>"] — up to 8 ids

Voice ids are 32-character hexadecimal strings. Browse the Fish Audio voice library at fish.audio — the id is the last path segment of fish.audio/m/<id>.

Multi-Speaker Dialogue

Pass an array of voice ids and mark speaker changes in text with <|speaker:N|> tags. The index in the tag selects the voice at that position in the array — <|speaker:0|> uses the first id, <|speaker:1|> the second, and so on.

{
  "text": "<|speaker:0|>Good morning!<|speaker:1|>Good morning! How are you?<|speaker:0|>I am great, thanks for asking!",
  "reference_id": [
    "933563129e564b19a115bedd57b7406a",
    "d13f84b987ad4f22b56d2b47f4eb838e"
  ],
  "format": "mp3"
}
  • Text before any tag is spoken by the first voice in the array.
  • A tag index with no matching array entry is not valid — supply one id per speaker index you use.
  • Passing two or more ids without any <|speaker:N|> tag returns 400, because every line would otherwise be rendered in the first voice.
  • For a single speaker, pass the id as a plain string — tags are not needed.

Emotion & effect markers

Place markers inline in text using square brackets. They are interpreted as performance direction and are not spoken aloud.

CategoryExamples
Emotions[happy], [sad], [angry], [excited], [calm], [nervous], [confident], [sarcastic]
Tone[whispering], [shouting], [soft tone], [in a hurry tone], [emphasis]
Audio effects[laughing], [chuckling], [sighing], [gasping], [panting], [clear throat]
Special[audience laughing], [background laughter], [break], [long-break]

Markers are not restricted to this list — any descriptive phrase works, for example [whispers sweetly] or [laughing nervously]. Use at most three emotion markers per sentence, and place sentence-level direction at the start of the sentence. Markers add no character cost and no latency.

Example Request

{
  "text": "[whispering] When you are building something new, there is this mix of wonder and fear. [chuckling] But that is what makes it worth building.",
  "reference_id": "933563129e564b19a115bedd57b7406a",
  "format": "mp3",
  "mp3_bitrate": 128
}

Example Response

{
  "request_id": "fish-audio-s2-1-pro_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "QUEUED",
  "polling_url": "https://gateway.pixazo.ai/v2/requests/status/fish-audio-s2-1-pro_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

Request Headers

HeaderRequiredDescription
Content-TypeYesapplication/json
Ocp-Apim-Subscription-KeyYesYour API subscription key.
X-Webhook-URLNoEnable Webhook callbacks (see Webhook section).

Response Handling

Status CodeMeaning
202Accepted — request queued; returns request_id and polling_url.
400Bad request — invalid or missing parameters: missing text, text over 5000 characters, a malformed reference_id, mp3_bitrate sent with format: "wav", or two or more reference_id values without <|speaker:N|> tags. Also returned for references and model — see “Parameters not supported” above.
401Unauthorized — missing or invalid subscription key.
402Insufficient balance.
403Forbidden.
429Too many requests.
500Internal server error.

Error Responses

{
  "error": "Insufficient Balance",
  "message": "Your wallet does not have enough balance."
}

A failed generation is reported via the status endpoint (or Webhook) with status: "ERROR" and an error message — for example Reference not found when reference_id does not name an existing voice.

Retrieving Results

Poll the status endpoint with the request_id from the submit response until status is COMPLETED (or FAILED/ERROR).

curl 'https://gateway.pixazo.ai/v2/requests/status/fish-audio-s2-1-pro_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' \
  -H 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY'

Completed response

{
  "request_id": "fish-audio-s2-1-pro_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "COMPLETED",
  "model_id": "fish-audio-s2-1-pro",
  "output": {
    "media_url": "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/fish-audio/1785407392754-66f0f615.mp3",
    "media_type": "audio/mpeg"
  },
  "created_at": "2026-07-30T10:29:44.313Z",
  "completed_at": "2026-07-30T10:29:53.391Z"
}

Response Fields

FieldTypeDescription
request_idstringUnique request identifier.
statusstringQUEUED, PROCESSING, COMPLETED, FAILED, or ERROR.
model_idstringThe model that handled the request.
output.media_urlstringPermanent URL of the generated audio.
output.media_typestringAudio MIME type (audio/mpeg for mp3, audio/wav for wav).
created_atstringRequest creation timestamp.
completed_atstringCompletion timestamp.
polling_urlstringStatus URL for this request.
errorstringError message when status is FAILED/ERROR.

Status Values & Flow

QUEUED → PROCESSING → COMPLETED (success) or FAILED/ERROR (failure).

Pricing

Billed at $0.02 per 1,000 UTF-8 bytes of input text, rounded up to the next whole 1,000. Every request bills at least one block.

Billing is measured in UTF-8 bytes, not characters. For English and other Latin-script text the two are the same — one character is one byte — but characters outside the ASCII range take more than one byte, so the same character count costs more in those languages.

InputCharactersUTF-8 bytesBlocksCost
English5005001$0.02
English1,2001,2002$0.04
Chinese / Japanese (3 bytes per character)4161,2482$0.04
Maximum single request, English5,0005,0005$0.10

The 5,000-character limit on text counts characters, not bytes — and characters outside the Basic Multilingual Plane (most emoji) count as two toward it. The most expensive possible request is therefore 5,000 three-byte characters, such as Chinese or Japanese: 15,000 bytes, or $0.30. Text made of 4-byte characters hits the limit at 2,500 characters, or $0.20.

Fish Audio S2.1 Pro API Pricing

No data available

Could not load current pricing

3. Fish Audio Speech to Text

Fish Audio Speech to Text API Documentation

Transcribe audio into text with automatic language detection and optional word-level timestamps. Asynchronous: submit returns a request_id; poll the status endpoint until the request is COMPLETED. The transcript is returned as a JSON file.

POST https://gateway.pixazo.ai/fish-audio-speech-to-text/v1/speech-to-text

Authentication

All requests require an API key passed via header.

HeaderTypeRequiredDescription
Ocp-Apim-Subscription-KeystringYesYour API subscription key

Speech to Text - Fish Audio

Request Code

POST https://gateway.pixazo.ai/fish-audio-speech-to-text/v1/speech-to-text
Content-Type: application/json
Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY

{
  "audio_url": "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/doc-assets/audio/speech-17s.mp3",
  "language": "en",
  "ignore_timestamps": false
}
import requests

url = "https://gateway.pixazo.ai/fish-audio-speech-to-text/v1/speech-to-text"
headers = {
    "Content-Type": "application/json",
    "Ocp-Apim-Subscription-Key": "YOUR_SUBSCRIPTION_KEY"
}
data = {
  "audio_url": "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/doc-assets/audio/speech-17s.mp3",
  "language": "en",
  "ignore_timestamps": false
}

resp = requests.post(url, json=data, headers=headers)
print(resp.json())
const res = await fetch("https://gateway.pixazo.ai/fish-audio-speech-to-text/v1/speech-to-text", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Ocp-Apim-Subscription-Key": "YOUR_SUBSCRIPTION_KEY"
  },
  body: JSON.stringify({
  "audio_url": "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/doc-assets/audio/speech-17s.mp3",
  "language": "en",
  "ignore_timestamps": false
})
});
console.log(await res.json());
curl -X POST 'https://gateway.pixazo.ai/fish-audio-speech-to-text/v1/speech-to-text' \
  -H 'Content-Type: application/json' \
  -H 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY' \
  --data-raw '{"audio_url": "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/doc-assets/audio/speech-17s.mp3", "language": "en", "ignore_timestamps": false}'

Output

{
  "request_id": "fish-audio-speech-to-text_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "QUEUED",
  "polling_url": "https://gateway.pixazo.ai/v2/requests/status/fish-audio-speech-to-text_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

Webhook (Optional)

Instead of polling, you can receive a Webhook callback when the request reaches a terminal state. Provide a Webhook URL via header on the submit request.

HeaderRequiredDescription
X-Webhook-URLTo enableHTTPS URL to receive the Webhook callback.
X-Webhook-ModeNoterminal (default, one callback on COMPLETED/ERROR) or sync (per-poll callbacks).

Example: enable Webhook

curl -X POST 'https://gateway.pixazo.ai/fish-audio-speech-to-text/v1/speech-to-text' \
  -H 'Content-Type: application/json' \
  -H 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY' \
  -H 'X-Webhook-URL: https://your-server.com/webhook' \
  --data-raw '{"audio_url": "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/doc-assets/audio/speech-17s.mp3", "language": "en", "ignore_timestamps": false}'

Callback Payload (success)

{
  "request_id": "fish-audio-speech-to-text_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "COMPLETED",
  "model_id": "fish-audio-speech-to-text",
  "output": {
    "media_url": "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/v1/{request_id}/output.json",
    "media_type": "application/json"
  },
  "created_at": "2026-07-30T14:49:39.898Z",
  "completed_at": "2026-07-30T14:49:59.000Z"
}

Failure callback shape

{
  "request_id": "fish-audio-speech-to-text_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "ERROR",
  "model_id": "fish-audio-speech-to-text",
  "error": "Description of the failure"
}

Delivery semantics

  • terminal mode: one Webhook callback when the request is COMPLETED or ERROR.
  • sync mode: a Webhook callback on each status change.
  • Callbacks are idempotent on request_id — de-duplicate on it.
  • Respond 200 within a few seconds; the Webhook endpoint must be HTTPS.

Request Parameters

ParameterRequiredTypeDefaultAllowed values / rangeDescription
audio_urlYesstring—http(s) URL, file up to 25 MBPublic URL of the audio to transcribe. Common formats are supported, including mp3, wav, m4a, flac and ogg. The file is fetched by the gateway, so the URL must be reachable without authentication.
languageNostring— (auto-detected)One of 57 ISO codes: af (Afrikaans), ar (Arabic), hy (Armenian), az (Azerbaijani), be (Belarusian), bs (Bosnian), bg (Bulgarian), ca (Catalan), zh (Chinese), hr (Croatian), cs (Czech), da (Danish), nl (Dutch), en (English), et (Estonian), fi (Finnish), fr (French), gl (Galician), de (German), el (Greek), he (Hebrew), hi (Hindi), hu (Hungarian), is (Icelandic), id (Indonesian), it (Italian), ja (Japanese), kn (Kannada), kk (Kazakh), ko (Korean), lv (Latvian), lt (Lithuanian), mk (Macedonian), ms (Malay), mr (Marathi), mi (Maori), ne (Nepali), no (Norwegian), fa (Persian), pl (Polish), pt (Portuguese), ro (Romanian), ru (Russian), sr (Serbian), sk (Slovak), sl (Slovenian), es (Spanish), sw (Swahili), sv (Swedish), tl (Tagalog), ta (Tamil), th (Thai), tr (Turkish), uk (Ukrainian), ur (Urdu), vi (Vietnamese), cy (Welsh)Optional language hint. Omit it and the model detects the language automatically, returning what it found in the transcript. There is no auto option — sending a placeholder such as "auto-detected" is rejected with HTTP 400.
ignore_timestampsNobooleantruetrue, falseSet to false to include word-level segments with start and end times. Adds latency on audio shorter than 30 seconds.

Audio limits

  • The audio file must be at most 25 MB. Larger files are rejected before transcription starts.
  • audio_url must be an http or https URL and must be publicly reachable.
  • There is no fixed duration limit; cost scales with the length of the audio (see Pricing).

Transcript Format

The completed request returns output.media_url, a JSON file containing the transcript. Fetch that URL to read the result.

{
  "text": "Strong consistency test. This should complete in a few seconds, not sixty.",
  "language": "English",
  "language_code": "en",
  "duration": 5.8253125,
  "segments": [
    {
      "text": "Strong",
      "start": 0,
      "end": 0.56
    },
    {
      "text": "consistency",
      "start": 0.56,
      "end": 1.12
    }
  ]
}
FieldTypeDescription
textstringThe full transcript.
languagestringDetected language name, e.g. English.
language_codestringDetected language code, e.g. en.
durationnumberLength of the transcribed audio in seconds. This is the quantity you are billed on.
segmentsarrayWord-level entries of {text, start, end} with times in seconds. Empty unless ignore_timestamps is false.

Example Request

{
  "audio_url": "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/doc-assets/audio/speech-17s.mp3",
  "language": "en",
  "ignore_timestamps": false
}

Example Response

{
  "request_id": "fish-audio-speech-to-text_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "QUEUED",
  "polling_url": "https://gateway.pixazo.ai/v2/requests/status/fish-audio-speech-to-text_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

Request Headers

HeaderRequiredDescription
Content-TypeYesapplication/json
Ocp-Apim-Subscription-KeyYesYour API subscription key.
X-Webhook-URLNoEnable Webhook callbacks (see Webhook section).

Response Handling

Status CodeMeaning
202Accepted — request queued; returns request_id and polling_url.
400Bad request — missing audio_url, a non-http(s) URL, or a wrongly typed language / ignore_timestamps.
401Unauthorized — missing or invalid subscription key.
402Insufficient balance.
429Too many requests.
500Internal server error.

An unreachable audio_url, an oversized file, or a file that is not recognisable audio is reported through the status endpoint as status: "ERROR", not as a synchronous error. Failed requests are not billed.

Retrieving Results

Poll the status endpoint with the request_id from the submit response until status is COMPLETED (or FAILED/ERROR), then fetch output.media_url for the transcript.

curl 'https://gateway.pixazo.ai/v2/requests/status/fish-audio-speech-to-text_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' \
  -H 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY'

Completed response

{
  "request_id": "fish-audio-speech-to-text_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "COMPLETED",
  "model_id": "fish-audio-speech-to-text",
  "output": {
    "media_url": "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/v1/{request_id}/output.json",
    "media_type": "application/json"
  },
  "created_at": "2026-07-30T14:49:39.898Z",
  "completed_at": "2026-07-30T14:49:59.000Z"
}

Response Fields

FieldTypeDescription
request_idstringUnique request identifier.
statusstringQUEUED, PROCESSING, COMPLETED, FAILED, or ERROR.
model_idstringThe model that handled the request.
output.media_urlstringURL of the transcript JSON file.
output.media_typestringapplication/json.
created_atstringRequest creation timestamp.
completed_atstringCompletion timestamp.
errorstringError message when status is FAILED/ERROR.

Status Values & Flow

QUEUED → PROCESSING → COMPLETED (success) or FAILED/ERROR (failure).

Pricing

Billed at $0.006 per minute of audio ($0.36 per audio hour), rounded up to the next whole minute. Billing is based on the length of the audio you submit, not on the size of the transcript.

Audio lengthBilled minutesCost
6 seconds1$0.006
90 seconds2$0.012
10 minutes10$0.06
1 hour60$0.36

A hold is placed when the request is submitted, because the audio length is not known until transcription completes. The hold is reduced to the real cost once the duration is known, and released in full if the request fails.

Fish Audio Speech to Text API Pricing

No data available

Could not load current pricing

⚡ Performance

Live usage measured on Pixazo's gateway, split by model version. Generation time is how long a generation takes end-to-end (lower is better). Success rate is the percent of generations that complete (higher is better).

Show data for the last
Generations
100last 30d
~3 per day
Success rate
100.0%
of completed generations
Generation time
92.7savg
p95 92.7s
Requests
Sep 16max 100Oct 15
Fish Audio Drama 3Avg 3/day
Generation Time
Sep 16max 92.7sOct 15
Fish Audio Drama 3Avg 92.7s
Error Rate
Sep 16max 5.0%Oct 15
Fish Audio Drama 3Avg 0.0%

〰 Uptime

Percent of generations that succeeded over the selected period, per model version.

Avg. Success Rate (30d)
100.00%
across all generations of this model family
Uptime
Sep 16max 100%Oct 15
SuccessfulAvg 100.00%