Whisper API Proxy
WhisperAPIProxy is an OpenAI-compatible proxy for audio transcription. It accepts requests just like the OpenAI Whisper API (POST /v1/audio/transcriptions) and uses a local Whisper-WebUI first. If that service is unavailable or returns nothing, local CPU engines take over; OpenAI is only used as the last option and only for clients that are explicitly allowed to use it.
Perfect for Home Assistant Assist
With the HACS integration ha-openai-whisper-stt-api you can use the proxy as the speech-to-text engine in your Assist pipeline. Transcription runs entirely on your server; Home Assistant only has to forward the audio.
How it works
The proxy uses this fallback chain (the order can be adjusted via fallback_order):
- Whisper-WebUI – fast GPU transcription when the service is reachable at
whisper_webui_host. - Parakeet – local CPU engine (ONNX), more accurate with numbers and technical terms, about 640 MB.
- Whistle – local CPU engine, only about 17 MB, very fast, 7 languages.
- OpenAI – only used when
allow_openai = Trueis set for the client and an API key is configured.
Local voice control without a GPU
Parakeet and Whistle are excellent for CPU execution and do not need a graphics card. This enables local voice control even on simple devices such as mini PCs or thin clients – depending on the hardware, transcription takes a little longer than on a GPU.
Usage is tracked per client in client_logs/. Optionally, the proxy stores recordings as Opus files together with their transcripts.
Requirements
- The proxy runs as a Docker container. You do not need to prepare a
config.py: if it is missing, the container creates a default configuration fromconfig.example.pyon first start. - Optional: a running Whisper-WebUI on port
7860(see the project link). Without it, the proxy keeps working through the CPU engines, just more slowly. - On first start the proxy downloads the local models in the background (about 660 MB in total). You can trigger this beforehand with
warmup_models.py. ffmpegis already included in the image.
Installation
1. Create directories
cd ~/ei23-docker
mkdir -p volumes/whisper-proxy/{config,recordings,client_logs,proxy_logs,models/hf,models/cactus}
The config.py is created automatically in volumes/whisper-proxy/config/ on first start.
2. Add the template and start
Add the template to your docker-compose.yml and start the container with ei23 dc (or docker compose up -d). Follow the logs with docker logs -f whisper-api-proxy.
Adjust the generated configuration
The automatically generated config.py contains demo clients and placeholder keys. Change them before productive use – at least the API keys in api_clients.
3. Adjust the configuration
Adjust whisper_webui_host, the api_clients and optionally the OpenAI key (see Configuration). Then restart the container:
Alternatively, run ei23 dc, which restarts all containers.
Template
whisper-proxy:
image: ghcr.io/ei23fxg/whisperapiproxy
container_name: whisper-api-proxy
restart: unless-stopped
ports:
- "5431:5431"
environment:
NEEDLE_TELEMETRY: "0"
TZ: Europe/Berlin
# Directory mount: the container writes config.py itself on first start.
CONFIG_PATH: /app/config/config.py
volumes:
- ./volumes/whisper-proxy/config:/app/config
- ./volumes/whisper-proxy/recordings:/app/recordings
- ./volumes/whisper-proxy/client_logs:/app/client_logs
- ./volumes/whisper-proxy/proxy_logs:/app/logs
- ./volumes/whisper-proxy/models/hf:/root/.cache/huggingface
- ./volumes/whisper-proxy/models/cactus:/root/.cache/cactus-needle
Configuration
Open ~/ei23-docker/volumes/whisper-proxy/config/config.py (created automatically on first start) and adjust the important values. You can keep the rest of the file – especially whisper_predict_config – unchanged for now.
# Address of the local Whisper-WebUI
whisper_webui_host = "http://192.168.1.10:7860/"
# Local CPU engines as fallback
fallback_order = ["parakeet", "whistle"]
parakeet_enabled = True
whistle_enabled = True
whistle_language = "de" # None = automatic language detection
# How often the Whisper-WebUI is checked in the background (seconds)
local_check_interval = 30
# OpenAI is only needed if a client has allow_openai = True
openai_api_key = "sk-proj-..."
openai_api_url = "https://api.openai.com/v1/audio/transcriptions"
# Clients: the key is the client ID used in logs and file names
api_clients = {
"homeassistant": {
"api_key": "sk-whisper-ha",
"save_recordings": False,
"allow_openai": False,
},
}
| Setting | Meaning |
|---|---|
whisper_webui_host | URL of the Whisper-WebUI. Important: inside the container, localhost is the proxy itself – use the server's LAN IP, e.g. http://192.168.1.10:7860/. |
fallback_order | Order in which the local engines are tried before OpenAI. |
parakeet_enabled / whistle_enabled | Individual engines can be disabled. |
whistle_language | Language for Whistle, e.g. "de". None = automatic detection. |
local_check_interval | Check interval for the Whisper-WebUI. Determines how quickly the proxy switches back to the WebUI after an outage. |
openai_api_key | Only required if a client is allowed to use OpenAI. |
api_clients | API keys and permissions per client. save_recordings stores the recording and transcript, allow_openai permits the paid fallback. |
Language
The proxy does not evaluate the language field sent by the Home Assistant integration. Set the language in the Whisper-WebUI or via whistle_language in config.py. Parakeet detects the language automatically.
Models and testing
The models are loaded in the background on first start. You can preload them so the first request does not have to wait:
Endpoints
| Endpoint | Purpose |
|---|---|
GET /health | Health check, no API key required |
POST /v1/audio/transcriptions | Transcription (Bearer API key required) |
GET /v1/local_service_status | Status of Whisper-WebUI, Parakeet and Whistle (API key required) |
GET /usage | Daily usage statistics per client |
Test it
# Health check
curl http://192.168.1.10:5431/health
# Transcription
curl http://192.168.1.10:5431/v1/audio/transcriptions \
-H "Authorization: Bearer sk-whisper-ha" \
-F "file=@recording.wav" \
-F "model=whisper-1"
The response looks like the OpenAI API:
Integrating into Home Assistant
The OpenAI Whisper Cloud integration by fabio-garavini supports OpenAI, GroqCloud and Mistral as well as any custom OpenAI-compatible endpoint – which is exactly what the Whisper API Proxy provides.
Integration, not an add-on
This extension is a HACS integration and runs directly inside Home Assistant. That is why it also works with the ei23 Docker installation, which does not use Supervisor add-ons.
1. Install the integration
The easiest way is via HACS:
- HACS → three-dot menu in the top right → Custom repositories
- Add
https://github.com/fabio-garavini/ha-openai-whisper-stt-apiwith category Integration - Search for OpenAI Whisper Cloud and install it
- Restart Home Assistant
Alternatively, copy the custom_components/openai_whisper_cloud folder from the repository into ~/ei23-docker/volumes/homeassistant/config/custom_components/ and restart HA.
2. Set up the integration
- Settings → Devices & services → Add integration
- Search for OpenAI Whisper Cloud
- Choose Custom as the provider
- Enter the following values:
| Field | Value |
|---|---|
| Name | e.g. Whisper Proxy |
| URL | http://192.168.1.10:5431 |
| API key | the key from api_clients, e.g. sk-whisper-ha |
| Model | whisper-1 |
| Temperature | 0 |
| Prompt | leave empty |
| Streaming upload | keep enabled |
| File field | file |
The URL without a path is correct: the integration appends /v1/audio/transcriptions automatically. A full endpoint URL is used unchanged – both work.
Model name
The proxy only uses the model name for the OpenAI fallback. whisper-1 is the default and always fits.
Temperature and prompt
The Temperature and Prompt fields of the integration are not evaluated by the proxy. They only affect providers that support these parameters themselves.
Streaming upload
The integration uploads audio while it is still being recorded. The proxy processes the completed upload normally afterwards. If this causes problems with your setup, disable Streaming upload in the integration options.
3. Switch the Assist pipeline
- Settings → Voice assistants
- Select your pipeline (or create a new one)
- Under Speech-to-text, select the newly created
Whisper Proxyentity - Save and test the pipeline
From now on, every voice command from Assist is sent to the proxy. Recognition runs on your server; OpenAI is only contacted if you explicitly allowed it for the client.
Home Assistant on the host network
The ei23 HA template uses network_mode: host, so http://127.0.0.1:5431 works as well. The LAN IP is still the more robust choice if you run HA differently later.
Data and logs
| Path | Content |
|---|---|
volumes/whisper-proxy/config/config.py | Configuration with API keys and clients (created on first start) |
volumes/whisper-proxy/recordings/ | Recordings as .opus and transcripts as .txt (if save_recordings is enabled) |
volumes/whisper-proxy/client_logs/ | Usage statistics per client |
volumes/whisper-proxy/proxy_logs/error.log | Proxy error log |
volumes/whisper-proxy/models/hf/ | Parakeet model (~640 MB) |
volumes/whisper-proxy/models/cactus/ | Whistle model (~17 MB) |
For a backup, config/config.py, client_logs/ and optionally the recordings are enough. The models are downloaded again when needed.
Notes
- Port
5431is reachable on all network interfaces. Restrict it within your LAN via firewall and do not expose it to the internet without protection. - The automatically generated
config.pycontains demo clients and placeholder keys. Change it before productive use. - The container runs as root; files it creates – including
config/config.py– will be owned by root afterwards. Edit the file withsudo(e.g.sudo nano ~/ei23-docker/volumes/whisper-proxy/config/config.py). Alternatively, take over the folder withsudo chown -R $USER:$USER volumes/whisper-proxy/config; restart the container afterwards if needed. NEEDLE_TELEMETRY=0disables telemetry of the Whistle engine.- Without a GPU, the proxy is still perfectly usable for dictation and Assist; the CPU engines take over automatically.
- The proxy is not a replacement for the Whisper-WebUI but the unified API in front of it. The WebUI can keep running in parallel at
http://[IP]:7860.
Troubleshooting
| Problem | Solution |
|---|---|
401 Invalid API Key | The API key in HA must exactly match an entry in api_clients – not the demo key from the template. |
500 Transcription failed | All engines failed. Check docker logs whisper-api-proxy and whether the models have been loaded. |
| Connection error while setting up in HA | Is the container running? Test curl http://[IP]:5431/health, check the firewall and port. |
| First request takes a long time | The models are loaded once (about 660 MB). Run warmup_models.py beforehand. |
config.py is not created | Check that volumes/whisper-proxy/config/ exists and is writable. The container log shows the path in use after Config:. |
Changes to config.py have no effect | Restart the container: docker compose restart whisper-proxy. |
Old volumes/whisper-proxy/config.py directory | May be left over from an earlier file mount. Delete it – the template now uses config/config.py. |
| Whisper-WebUI is not used | whisper_webui_host is set to localhost. Inside the container it must point to the server's LAN IP. |
| No recordings | Enable save_recordings for the client and check write permissions of the folders. |
HA reports connection_error | Use the URL without a path (http://[IP]:5431) and check the key. The proxy has no /v1/models; the integration accepts that. |