DEV Community

Preecha
Preecha

Posted on

How to Use Fish Audio S2 API: A Complete Guide with Apidog

How to Integrate and Test the Fish Audio S2 API

Fish Audio S2 API is a production-grade text-to-speech REST API powered by a 4-billion-parameter model trained on 10 million hours of audio. It supports voice cloning, streaming, and 50+ languages. This guide shows how to send requests, manage voice references, stream audio, and test each endpoint with Python and Apidog.

Try Apidog today

Introduction

Modern text-to-speech models can whisper, laugh, change tone mid-sentence, and generate multilingual speech. Fish Speech S2-Pro provides these capabilities through the Fish Audio S2 API.

A reliable integration requires more than a single POST request. You need to configure authentication, handle binary audio responses, manage reference audio, consume streaming data correctly, and test the API contract before deploying.

Before making your first request, download Apidog to inspect payloads, test emotion tags, validate streaming responses, and play generated audio directly from the response panel.

What Is the Fish Audio S2 API?

The Fish Audio S2 API is the HTTP interface to Fish Speech S2-Pro, an open-source TTS system built around a Dual-Autoregressive architecture.

The model separates:

  • Semantic generation: a 4B-parameter autoregressive model operating along the time axis.
  • Residual codebook generation: a 400M-parameter autoregressive model operating along the depth axis.

This architecture enables high-quality synthesis with a reported real-time factor of 0.195 on a single NVIDIA H200.

Core capabilities

Feature Details
Languages Approximately 50, including English, Chinese, Japanese, Korean, Arabic, French, and German
Voice cloning Uses 10–30 seconds of reference audio without fine-tuning
Emotion control Natural-language tags such as [laugh], [whispers], and [super happy]
Multi-speaker generation Native `<\
Streaming Real-time chunked audio with {% raw %}"streaming": true
Output formats WAV, MP3, and PCM
Authentication Bearer token through the Authorization header

For a local deployment, the default base URL used in this guide is:

http://127.0.0.1:8080
Enter fullscreen mode Exit fullscreen mode

API endpoints are available under the /v1/ namespace.

Set Up the Fish Audio S2 API

Prerequisites

You need:

  1. A deployed Fish Speech S2-Pro server.
  2. Model and decoder checkpoints.
  3. An API key.
  4. An HTTP client that can handle binary audio responses.

Start the API server

Run:

python tools/api_server.py \
  --llama-checkpoint-path checkpoints/s2-pro \
  --decoder-checkpoint-path checkpoints/s2-pro/codec.pth \
  --listen 0.0.0.0:8080 \
  --compile \
  --half \
  --api-key YOUR_API_KEY \
  --workers 4
Enter fullscreen mode Exit fullscreen mode

The relevant options are:

  • --compile: enables torch.compile optimization. This can substantially reduce inference latency but adds a one-time warmup cost.
  • --half: enables FP16 to reduce GPU memory usage.
  • --api-key: configures the Bearer token required by clients.
  • --workers: configures the number of API workers.

Verify the server

Call the health endpoint:

curl http://127.0.0.1:8080/v1/health
Enter fullscreen mode Exit fullscreen mode

Expected response:

{
  "status": "ok"
}
Enter fullscreen mode Exit fullscreen mode

If authentication is required for the health endpoint in your deployment, include the Bearer token:

curl \
  -H "Authorization: Bearer YOUR_API_KEY" \
  http://127.0.0.1:8080/v1/health
Enter fullscreen mode Exit fullscreen mode

Configure the API in Apidog

Create a new HTTP project in Apidog, then configure an environment variable for the base URL:

http://127.0.0.1:8080
Enter fullscreen mode Exit fullscreen mode

Add this global request header:

Authorization: Bearer YOUR_API_KEY
Enter fullscreen mode Exit fullscreen mode

Saving authentication at the environment level avoids copying the token into every request. It also lets you switch between local, staging, and production environments without editing individual endpoints.

Send Your First TTS Request

The primary synthesis endpoint is:

POST /v1/tts
Enter fullscreen mode Exit fullscreen mode

Test it with cURL

curl \
  --request POST \
  --url http://127.0.0.1:8080/v1/tts \
  --header "Authorization: Bearer YOUR_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "text": "Hello! This is a test of the Fish Audio S2 API.",
    "format": "wav",
    "streaming": false,
    "temperature": 0.8,
    "top_p": 0.8,
    "repetition_penalty": 1.1,
    "max_new_tokens": 1024
  }' \
  --output output.wav
Enter fullscreen mode Exit fullscreen mode

Play or inspect output.wav to verify the response.

Test it in Apidog

Create a POST request for:

http://127.0.0.1:8080/v1/tts
Enter fullscreen mode Exit fullscreen mode

Set the body type to JSON and use:

{
  "text": "Hello! This is a test of the Fish Audio S2 API.",
  "format": "wav",
  "streaming": false,
  "temperature": 0.8,
  "top_p": 0.8,
  "repetition_penalty": 1.1,
  "max_new_tokens": 1024
}
Enter fullscreen mode Exit fullscreen mode

Send the request. The endpoint returns raw audio bytes. Apidog detects the audio response and provides an inline player, allowing you to verify the generated speech without writing client code.

TTS request parameters

Parameter Type Default Description
text string Required Text to synthesize
format string "wav" Output format: wav, mp3, or pcm
chunk_length integer 200 Synthesis chunk size, typically 100300
seed integer null Fixed seed for reproducible output
streaming boolean false Returns audio in real-time chunks
max_new_tokens integer 1024 Maximum number of tokens to generate
temperature float 0.8 Sampling randomness, typically 0.11.0
top_p float 0.8 Nucleus sampling threshold, typically 0.11.0
repetition_penalty float 1.1 Penalizes repeated sequences, typically 0.92.0
use_memory_cache string "off" Controls in-memory reference encoding cache

Clone a Voice with Reference Audio

Fish Audio S2 supports zero-shot voice cloning. Provide a short reference audio clip and a matching transcript, then use the resulting reference ID in later TTS requests.

Only use reference audio that you have permission to process and clone.

Encode the reference audio

The request expects base64-encoded audio. Encode a WAV file with Python:

import base64
from pathlib import Path

audio_bytes = Path("reference.wav").read_bytes()
audio_base64 = base64.b64encode(audio_bytes).decode("utf-8")

Path("reference.txt").write_text(audio_base64)
Enter fullscreen mode Exit fullscreen mode

Add the reference

Send a request to:

POST /v1/references/add
Enter fullscreen mode Exit fullscreen mode

Example body:

{
  "id": "my-voice-clone",
  "text": "This is the reference transcription matching the audio.",
  "audio": "<base64-encoded-wav-bytes>"
}
Enter fullscreen mode Exit fullscreen mode

Expected response:

{
  "success": true,
  "message": "Reference added successfully",
  "reference_id": "my-voice-clone"
}
Enter fullscreen mode Exit fullscreen mode

In Apidog, create the request with a JSON body and replace the audio value with the encoded audio data.

Generate speech with the reference

Pass the stored reference_id to /v1/tts:

{
  "text": "This sentence will be spoken in the cloned voice.",
  "reference_id": "my-voice-clone",
  "format": "mp3"
}
Enter fullscreen mode Exit fullscreen mode

Save this request as a reusable template when testing multiple voices. You can then compare different references by changing only reference_id.

Test the Fish Audio S2 API with Python

API contract tests catch problems such as:

  • Missing or deleted reference IDs.
  • Invalid sampling parameters.
  • Incorrect content types.
  • Empty or truncated audio.
  • Authentication failures.
  • Streaming responses consumed incorrectly.

Install the test dependencies:

python -m pip install pytest httpx
Enter fullscreen mode Exit fullscreen mode

Set the API configuration through environment variables:

export FISH_AUDIO_BASE_URL="http://127.0.0.1:8080"
export FISH_AUDIO_API_KEY="YOUR_API_KEY"
Enter fullscreen mode Exit fullscreen mode

On PowerShell:

$env:FISH_AUDIO_BASE_URL = "http://127.0.0.1:8080"
$env:FISH_AUDIO_API_KEY = "YOUR_API_KEY"
Enter fullscreen mode Exit fullscreen mode

Create test_fish_audio_s2_api.py:

import base64
import os
from pathlib import Path

import httpx

BASE_URL = os.getenv("FISH_AUDIO_BASE_URL", "http://127.0.0.1:8080")
API_KEY = os.environ["FISH_AUDIO_API_KEY"]
HEADERS = {"Authorization": f"Bearer {API_KEY}"}

class TestFishAudioS2API:
    """API contract tests for Fish Audio S2 endpoints."""

    def test_health_check(self):
        response = httpx.get(
            f"{BASE_URL}/v1/health",
            headers=HEADERS,
            timeout=10,
        )

        assert response.status_code == 200
        assert response.json()["status"] == "ok"

    def test_tts_returns_wav_audio(self):
        payload = {
            "text": "Unit test: verifying Fish Audio S2 API output.",
            "format": "wav",
            "seed": 42,
        }

        response = httpx.post(
            f"{BASE_URL}/v1/tts",
            json=payload,
            headers=HEADERS,
            timeout=60,
        )

        assert response.status_code == 200
        assert response.headers["content-type"].startswith("audio/wav")
        assert len(response.content) > 1000

    def test_tts_rejects_invalid_temperature(self):
        payload = {
            "text": "Test invalid temperature.",
            "temperature": 99.0,
        }

        response = httpx.post(
            f"{BASE_URL}/v1/tts",
            json=payload,
            headers=HEADERS,
            timeout=30,
        )

        assert response.status_code == 422

    def test_reference_lifecycle(self):
        reference_id = "unit-test-voice"
        audio_path = Path("test_reference.wav")
        audio_base64 = base64.b64encode(
            audio_path.read_bytes()
        ).decode("utf-8")

        try:
            add_response = httpx.post(
                f"{BASE_URL}/v1/references/add",
                json={
                    "id": reference_id,
                    "text": "This is a unit test reference audio.",
                    "audio": audio_base64,
                },
                headers=HEADERS,
                timeout=30,
            )

            assert add_response.status_code == 200
            assert add_response.json()["success"] is True

            list_response = httpx.get(
                f"{BASE_URL}/v1/references/list",
                headers=HEADERS,
                timeout=30,
            )

            assert list_response.status_code == 200
            assert reference_id in list_response.json()["reference_ids"]

        finally:
            httpx.request(
                "DELETE",
                f"{BASE_URL}/v1/references/delete",
                json={"reference_id": reference_id},
                headers=HEADERS,
                timeout=30,
            )
Enter fullscreen mode Exit fullscreen mode

Place a valid audio fixture named test_reference.wav in the same directory, then run:

pytest test_fish_audio_s2_api.py -v
Enter fullscreen mode Exit fullscreen mode

Using a fixed seed makes the sampling configuration reproducible. Avoid asserting that the entire audio file is byte-for-byte identical unless your deployment guarantees deterministic output across its hardware and software configuration.

Run Automated Tests in Apidog

You can run the same endpoint checks through Apidog's Test Scenarios feature:

  1. Open your Fish Audio S2 API collection.
  2. Select Test Scenarios.
  3. Create a new scenario.
  4. Add requests in this order:
    • Health check.
    • TTS request.
    • Add reference.
    • List references.
    • Delete reference.
  5. Add assertions to the TTS request:
    • Response status equals 200.
    • Content-Type contains audio.
    • Response body is not empty.
    • Response time is below your chosen threshold.
  6. Run the scenario and inspect the result for each step.

For the reference flow, extract the created reference ID or use a dedicated test ID. Always include a delete request so test data does not accumulate between runs.

Apidog reports assertion results and response timings, allowing the scenario to be reused during development or as part of a CI-triggered API test workflow.

Stream Audio in Real Time

For live playback, set "streaming": true and consume the response incrementally.

import os

import httpx

base_url = os.getenv(
    "FISH_AUDIO_BASE_URL",
    "http://127.0.0.1:8080",
)
api_key = os.environ["FISH_AUDIO_API_KEY"]

with httpx.stream(
    "POST",
    f"{base_url}/v1/tts",
    json={
        "text": "Streaming audio from the Fish Audio S2 API in real time.",
        "format": "wav",
        "streaming": True,
    },
    headers={
        "Authorization": f"Bearer {api_key}",
    },
    timeout=None,
) as response:
    response.raise_for_status()

    with open("streamed_output.wav", "wb") as audio_file:
        for chunk in response.iter_bytes(chunk_size=4096):
            if chunk:
                audio_file.write(chunk)
Enter fullscreen mode Exit fullscreen mode

The server begins returning audio before synthesis is complete. The reported time-to-first-audio is approximately 100ms, making streaming suitable for interactive voice applications.

When integrating streaming into an application:

  • Call raise_for_status() before processing chunks.
  • Ignore empty chunks.
  • Do not load the entire response into memory.
  • Use timeout=None or a timeout appropriate for long synthesis requests.
  • Confirm that your playback layer supports the selected audio format.

Add Inline Emotion Control

Emotion and delivery instructions are included directly in the text field:

{
  "text": "[whispers] The secret is hidden here. [super happy] I found it!",
  "format": "wav"
}
Enter fullscreen mode Exit fullscreen mode

No additional request parameter is required. The model interprets supported bracketed tags as prosody instructions.

Examples from the Fish Speech source include:

[laugh]
[cough]
[pitch up]
[professional broadcast tone]
[whisper in small voice]
Enter fullscreen mode Exit fullscreen mode

Test tags individually before combining them. This makes it easier to determine whether an unexpected result comes from a specific instruction or from the interaction between multiple tags.

Integration Checklist

Before shipping a Fish Audio S2 integration, verify the following:

  • [ ] The API key is stored outside source control.
  • [ ] The health endpoint is monitored.
  • [ ] TTS requests have explicit client timeouts.
  • [ ] Binary responses are written or streamed without text decoding.
  • [ ] The response Content-Type matches the requested format.
  • [ ] Reference audio has a matching transcript.
  • [ ] Reference IDs are created, listed, and deleted correctly.
  • [ ] Invalid parameters return expected validation errors.
  • [ ] Streaming clients handle partial and empty chunks.
  • [ ] Test references are removed after automated test runs.
  • [ ] Voice cloning is limited to audio you are authorized to use.

Conclusion

The Fish Audio S2 API exposes text-to-speech, zero-shot voice cloning, emotion control, and real-time streaming through a REST interface.

A reliable implementation should:

  1. Configure Bearer authentication at the client or environment level.
  2. Validate binary audio responses and content types.
  3. Manage the full reference lifecycle.
  4. Consume streaming responses incrementally.
  5. Maintain automated tests for both valid and invalid requests.

Use Apidog to send requests, inspect and play binary audio responses, save reusable API examples, and run automated endpoint scenarios without first building a custom test interface.

FAQ

What is the Fish Audio S2 API?

The Fish Audio S2 API is the REST interface to Fish Speech S2-Pro, a 4B-parameter text-to-speech model trained on 10 million hours of audio. It supports voice cloning, streaming, emotion control, and 50+ languages through endpoints under /v1/.

How do I authenticate?

Send a Bearer token in the request headers:

Authorization: Bearer YOUR_API_KEY
Enter fullscreen mode Exit fullscreen mode

The server API key is configured at startup through the --api-key option. In Apidog, store the token at the environment level so it is automatically included in requests.

Can I test the API without writing test code?

Yes. Apidog Test Scenarios can send requests and validate status codes, headers, response times, and request sequences through a visual interface.

For repeatable CI checks, you can also use the pytest and httpx test suite shown above.

Which audio formats are supported?

The API returns WAV, MP3, or PCM audio. Select the output through the format field:

{
  "text": "Generate this as MP3.",
  "format": "mp3"
}
Enter fullscreen mode Exit fullscreen mode

WAV is the default.

How does voice cloning work?

Upload a 10–30 second reference clip and its matching transcript to:

POST /v1/references/add
Enter fullscreen mode Exit fullscreen mode

Then pass the returned ID to /v1/tts:

{
  "text": "Generate speech using the stored voice.",
  "reference_id": "my-voice-clone"
}
Enter fullscreen mode Exit fullscreen mode

The API clones the voice without additional fine-tuning.

What is the reported real-time factor?

On a single NVIDIA H200, Fish Audio S2 reports a real-time factor of 0.195 with streaming enabled. This corresponds to generating approximately five seconds of audio per second of compute. Reported time-to-first-audio is approximately 100ms.

How do I inspect audio responses in Apidog?

When the API returns binary audio, Apidog renders an inline audio player. You can listen to the result, inspect response headers, and add assertions from the same request panel.

Top comments (0)