# How to Set Up API Authentication with Token-Based Security in Fish Speech

> Learn how to set up API authentication with token-based security for your Fish Speech API. Secure your data by launching the server with an API key and using the Authorization header for requests.

- Repository: [Fish Audio/fish-speech](https://github.com/fishaudio/fish-speech)
- Tags: how-to-guide
- Published: 2026-03-12

---

**Secure your Fish Speech API by launching the server with `--api-key <token>` and sending requests with the `Authorization: Bearer <token>` header.**

Fish Speech provides a production-ready HTTP API server that supports **token-based authentication** via Bearer tokens. This security layer is implemented as ASGI middleware in the inference server, allowing you to protect text-to-speech endpoints without modifying your client code beyond adding a header. The authentication flow is defined in the server utilities and enforced globally across all routes when enabled.

## How Token-Based Authentication Works in Fish Speech

The authentication system uses a simple but effective **shared secret** pattern. When you start the server with a specific token, the middleware intercepts every incoming request and validates the `Authorization` header against that secret.

The flow works as follows:

1. **Server startup**: You provide a token via the `--api-key` argument in [`tools/server/api_utils.py`](https://github.com/fishaudio/fish-speech/blob/main/tools/server/api_utils.py)
2. **Middleware activation**: The `api_auth` function in [`tools/api_server.py`](https://github.com/fishaudio/fish-speech/blob/main/tools/api_server.py) wraps all routes with a verification layer
3. **Request validation**: Clients must include `Authorization: Bearer <your-token>` in every request header
4. **Access control**: If the token mismatches or is missing, the server returns HTTP 401 Unauthorized

If you omit the `--api-key` flag during startup, the server automatically disables authentication and accepts all requests.

## Configuring the API Key on the Server

### Command-Line Setup

The server accepts your authentication token through a command-line argument defined in [`tools/server/api_utils.py`](https://github.com/fishaudio/fish-speech/blob/main/tools/server/api_utils.py) (lines 21-42):

```python
parser.add_argument("--api-key", type=str, default=None)

```

To enable authentication, launch your server with the `--api-key` flag:

```bash
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 \
  --api-key mySuperSecret123

```

**Important**: Choose a cryptographically secure random string for production environments. The token is stored in memory only and never written to disk unless you include it in your startup scripts.

## Understanding the Authentication Middleware

The enforcement logic resides in [`tools/api_server.py`](https://github.com/fishaudio/fish-speech/blob/main/tools/api_server.py) (lines 33-45) within the `api_auth` function:

```python
def api_auth(endpoint):
    async def verify(token: Annotated[str, Depends(bearer_auth)]):
        if token != self.args.api_key:
            raise HTTPException(401, None, "Invalid token")
        return await endpoint()
    
    if self.args.api_key is not None:
        return verify
    else:
        return passthrough

```

This middleware performs three critical functions:

1. **Token extraction**: Uses `bearer_auth` dependency to parse the `Authorization` header
2. **Constant-time comparison**: Compares the provided token against `self.args.api_key` (the value from your `--api-key` argument)
3. **Error handling**: Returns HTTP 401 with "Invalid token" if verification fails

All routes—including `/v1/tts` and `/v1/vqgan/encode`—are automatically wrapped with this middleware when you provide an API key:

```python
self.routes = Routes(routes, http_middlewares=[api_auth])

```

## Making Authenticated Requests

Once the server is running with authentication enabled, every client request must include the Bearer token in the HTTP headers.

### Using curl

```bash
TOKEN=mySuperSecret123

curl -X POST http://127.0.0.1:8080/v1/tts \
     -H "Authorization: Bearer $TOKEN" \
     -H "Content-Type: application/msgpack" \
     --data-binary @request.msgpack \
     --output audio.wav

```

### Using the Python Client

Fish Speech provides a reference client in [`tools/api_client.py`](https://github.com/fishaudio/fish-speech/blob/main/tools/api_client.py) (lines 80-84) that automatically handles authentication when you provide the `--api_key` argument:

```python
headers={
    "authorization": f"Bearer {args.api_key}",
    "content-type": "application/msgpack",
}

```

Run the client with:

```bash
python tools/api_client.py \
    --text "Hello from Fish Speech!" \
    --api_key mySuperSecret123 \
    --url http://127.0.0.1:8080/v1/tts \
    --format wav

```

## Disabling Authentication for Local Development

For local testing or internal networks where security is not a concern, you can run the server without the `--api-key` flag:

```bash
python tools/api_server.py \
  --llama-checkpoint-path checkpoints/s2-pro \
  --decoder-checkpoint-path checkpoints/s2-pro/codec.pth \
  --listen 127.0.0.1:8080

```

When `args.api_key` is `None`, the `api_auth` middleware returns a `passthrough` function that immediately forwards requests to the endpoint without validation.

## Summary

- **Token-based security** in Fish Speech is enabled by starting the server with `--api-key <token>` in [`tools/server/api_utils.py`](https://github.com/fishaudio/fish-speech/blob/main/tools/server/api_utils.py)
- The **authentication middleware** in [`tools/api_server.py`](https://github.com/fishaudio/fish-speech/blob/main/tools/api_server.py) enforces Bearer token validation on every route, returning HTTP 401 for invalid or missing tokens
- Clients must include `Authorization: Bearer <token>` headers, as demonstrated in [`tools/api_client.py`](https://github.com/fishaudio/fish-speech/blob/main/tools/api_client.py)
- Omitting `--api-key` disables authentication entirely, creating a passthrough middleware suitable for local development

## Frequently Asked Questions

### What happens if I don't provide the Authorization header?

If the server was started with `--api-key` and you omit the `Authorization: Bearer <token>` header, the middleware in [`tools/api_server.py`](https://github.com/fishaudio/fish-speech/blob/main/tools/api_server.py) will raise an HTTPException with status code 401 and the message "Invalid token", rejecting your request before it reaches the TTS endpoint.

### Can I use multiple API keys?

The current implementation in [`tools/api_server.py`](https://github.com/fishaudio/fish-speech/blob/main/tools/api_server.py) only supports a single shared secret via the `--api-key` argument. If you need multiple keys for different clients, you would need to modify the `verify` function in the `api_auth` middleware to check against a list or database of valid tokens instead of a single `self.args.api_key` value.

### Is the token transmitted securely?

Security depends on your transport layer. The Fish Speech server itself handles the token in plaintext memory (comparing `token != self.args.api_key` in [`tools/api_server.py`](https://github.com/fishaudio/fish-speech/blob/main/tools/api_server.py)). To prevent interception, always run the server behind HTTPS/TLS in production environments, ensuring the `Authorization` header is encrypted during transit.

### Where is the API key stored?

The API key is **not stored persistently** by the Fish Speech server. It exists only in memory as `self.args.api_key` after being parsed from the command line in [`tools/server/api_utils.py`](https://github.com/fishaudio/fish-speech/blob/main/tools/server/api_utils.py). If you restart the server without the `--api-key` flag, the previous token becomes invalid and authentication is disabled.