Skip to content

OpenVoiceOS STT HTTP Server

In a nutshell

This is a small standalone program that turns any OVOS speech-to-text engine into a web service. Speech-to-text is the part that converts spoken audio into written text. Once it's running, other devices on your network (or the internet) can send it audio over a simple web request and get back the transcribed text. This lets one capable machine do the listening for many lightweight devices. It can even pretend to be popular cloud services (like OpenAI Whisper or Google), so software written for those works against your own server unchanged. See STT plugins and the Glossary.

Lightweight HTTP microservice for any OVOS speech‑to‑text plugin.

The OpenVoiceOS STT HTTP Server wraps your chosen OVOS STT plugin inside a FastAPI service, complete with automatic language detection. This makes it easy to deploy on your local machine, in Docker, or behind a load balancer.


Usage Guide

Install the server

pip install --pre "ovos-stt-http-server>=0.25.1a3"

Choose your STT plugin

You name the plugin to serve with the --engine flag (below). When no explicit config is passed, the server reads the plugin's own section from mycroft.conf (stt.<plugin-id>), matching OVOSSTTFactory behavior, so a mounted config file can select the model, device, and other plugin settings. With no config file present, the plugin runs with its built-in defaults.

A normal OVOS install (not this server) selects its STT plugin under the stt section instead:

{
 "stt": {
   "module": "ovos-stt-plugin-xxx",
   "ovos-stt-plugin-xxx": {
     "model": "xxx"
   }
 }
}

Launch the server

ovos-stt-server \
 --engine ovos-stt-plugin-xxx \
 --host 0.0.0.0 \
 --port 8080

Verify it's running

Visit http://localhost:8080/status in your browser or run:

curl http://localhost:8080/status

Command‑Line Options

$ ovos-stt-server --help
usage: ovos-stt-server [-h] --engine ENGINE [--lang-engine LANG_ENGINE] [--port PORT] [--host HOST] [--multi] [--mcp]

options:
  -h, --help            show this help message and exit
  --engine ENGINE       stt plugin to be used
  --lang-engine LANG_ENGINE
                        audio language detection plugin to be used
  --port PORT           port number
  --host HOST           host
  --multi               Load a plugin instance per language (force lang support)
  --mcp                 mount MCP server at /mcp (requires ovos-stt-http-server[mcp])

The short help text leaves out the defaults: the port defaults to 8080 and the host to 0.0.0.0, and --engine is the one required flag.


Technical Explanation

  • FastAPI core
    The server is a FastAPI app served by uvicorn, exposing REST endpoints.

  • Plugin wrapping
    --engine names any opm.stt plugin entry point (Whisper, Deepgram, and so on). It is loaded dynamically via the OVOS Plugin Manager. When no explicit config is passed, the server reads the plugin's own section from mycroft.conf (stt.<plugin-id>), matching OVOSSTTFactory behavior, so a mounted config file can select model, device, and other plugin settings.

  • Language detection
    --lang-engine names an opm.transformer.audio plugin implementing AudioLanguageDetector. When a /stt request passes lang=auto, audio is routed through it before transcription.

  • Multi-model mode
    --multi loads one engine instance per language on demand (one model per lang), instead of a single shared model.

  • Compatibility routers
    Beyond the native endpoints, the app mounts drop-in compatible routers so existing cloud-STT clients work unchanged. Examples are Wit.ai (POST /wit/speech, override the SDK host with the WIT_URL env var) and Chromium speech-api (POST /speech-api/v2/recognize). The app also routes for OpenAI Whisper, Whisper.cpp server, Deepgram, Google, AssemblyAI, Azure, IBM Watson, AWS Transcribe, Vosk, Speechmatics, Gladia, ElevenLabs Scribe, Groq, and Kaldi GStreamer. See /docs for the authoritative set. A GET /utcp manual advertises the endpoints to UTCP agents. An MCP endpoint mounts at /mcp on the same port when the server is started with --mcp (requires pip install --pre 'ovos-stt-http-server[mcp]>=0.26.0a1'). The extra alone no longer auto-mounts it.

    The mcp extra installs fastmcp, not the mcp SDK

    The extra keeps the name mcp, but it resolves the third-party fastmcp>=3,<4 package, not the official mcp SDK (MCP SDK 2.0 removed mcp.server.fastmcp.FastMCP, so a server still importing that symbol fails on the 2.x SDK). This server serves MCP with fastmcp; a client consuming a different MCP server (like ovos-mcp-toolbox, see Agent Tool Plugins) uses the official mcp SDK instead.

  • Scalability
    Stateless design lets you run multiple instances behind a load balancer or in Kubernetes.


HTTP API Endpoints

Native endpoints:

Endpoint Method Description
/status GET Returns {status, plugin, lang_plugin}.
/stt POST Raw audio bytes in the body (query: lang, sample_rate default 16000, sample_width default 2) → plain‑text transcript. With lang=auto, language is detected first.
/lang_detect POST Raw audio bytes (query: valid_langs) → JSON { "lang": "en", "conf": 0.83 }.
/utcp GET UTCP tool-discovery manual (JSON).
/docs GET Interactive FastAPI OpenAPI docs.

Compatibility routers (selection): POST /wit/speech (Wit.ai), POST /speech-api/v2/recognize (Chromium), plus OpenAI/Deepgram/Google/etc. See /docs for the full list.

OpenAI-Compatible Translation Endpoint

POST /openai/v1/audio/translations mirrors OpenAI's Whisper translations endpoint. OpenAI's contract for this endpoint always returns English, regardless of the spoken language. So after transcribing, the request runs an extra translate step using the configured OVOS translate plugin. If no translate plugin is configured, the endpoint falls back to returning the untranslated transcript instead of failing.

{
  "language": {
    "translation_module": "ovos-translate-plugin-server"
  }
}

Transformer Pipelines

The server can run OVOS transformer plugins around transcription. Both hooks live in the model containers, so every surface gets them: the native /stt endpoint, all vendor-compat routers, the websocket streaming routes, MCP, and even UTCP.

  • Audio transformers run before the STT stage. When the chain includes an AudioLanguageDetector (like the --lang-engine mentioned above), its detected language resolves lang=auto (an explicitly requested language always wins).
  • Utterance transformers rewrite the transcript after ASR, before it is returned.

Loading is config-gated and opt-in via the standard mycroft.conf sections. With no config the server behaves exactly as before:

{
  "audio_transformers": {
    "ovos-audio-transformer-plugin-speechbrain-langdetect": {}
  },
  "utterance_transformers": {
    "ovos-utterance-corrections-plugin": {}
  }
}

Chains run in ascending priority order. An explicit "order" list in a section wins over priorities. In multi-model mode (--multi) one set of transformer instances is shared across the per-language engines. See the transformer plugins reference for the full contract.

Server-side transforms are a client-invisible change

An utterance transformer on the server means clients receive a different transcript than what the STT engine actually heard. Use it deliberately: for fleet-wide vocabulary corrections, language routing via an AudioLanguageDetector, or server-side audio cleanup for thin clients. Never enable the same plugin on both the server and a downstream OVOS stack that also runs transformers, or it will run twice.


Companion Plugin

To point a OpenVoiceOS (or compatible project) to a STT server you can use the companion plugin.

Package name vs. repository name

The PyPI package is ovos-stt-plugin-server, but its source repository is named OpenVoiceOS/ovos-stt-server-plugin (the words are swapped). Both names refer to the same plugin. Use the pip name to install, the repo name to find the source.

Install

pip install ovos-stt-plugin-server

Upcoming — universal server adapter

A server_type option is planned for this companion plugin, so a single config shape can target different self-hosted or cloud STT server APIs without a dedicated plugin per vendor.

Key name: urls, not host

This STT companion plugin reads the urls key (a list of strings). The TTS companion plugin reads a different key, host. The two are not interchangeable. If you set the wrong key, the plugin does not error. It silently ignores the value and falls back to the public servers described below.

Configure

Point it at your own server (localhost, or wherever you run the container above):

  "stt": {
    "module": "ovos-stt-plugin-server",
    "ovos-stt-plugin-server": {
      "urls": ["http://localhost:8080/stt"],
      "verify_ssl": true,
      "user_agent": "my-ovos-client",
      "timeout": 5
    },
 }

Restart and verify

After editing the config, restart the client so it picks up the change, then confirm it is actually talking to your server:

# raspOVOS
ovos-restart

# any other systemd-managed install
systemctl --user restart ovos.service

Say a command and check the voice/audio logs, or watch live traffic with ovos-busmon, to confirm the configured urls server is the one receiving the request, not a public fallback.

for audio language detection

  "audio_transformers": {
      "ovos-audio-lang-server-plugin": {
        "urls": ["http://localhost:8080/lang_detect"],
        "verify_ssl": true
      }
  }

audio_transformers goes at the top level, not under listener

The plugin is constructed with no config, so it falls back to AudioTransformer._read_mycroft_conf(), which reads the top-level audio_transformers block only. A listener.audio_transformers block is read by the transformer service, which logs it as deprecated, but never reaches this plugin.

Put the block in the wrong place and urls is silently empty. The plugin then falls back to its built-in default and posts your audio to a public server on the internet. There is no error to notice.

The singular url key (a single string or a list) is also accepted as an alias for urls, and takes precedence over urls when both are set. The optional user_agent (STT client and lang-detect classifier alike) overrides the request User-Agent sent to servers. It defaults to the standard OVOS client UA.

No urls configured → public servers, not local failure

If you omit urls entirely, the plugin does not fail. It silently falls back to a small built-in list of public OVOS STT servers (shuffled, tried in order) run by community members. That's convenient for a quick test, but it means your audio leaves your network by default unless you set urls yourself. Always set urls explicitly for any real deployment.

Community servers are best-effort demos

The public OVOS servers exist for easy onboarding and demos only. They are best-effort, not optimized, carry no uptime guarantees, and may vanish at any time. OVOS will be slow and unreliable if you rely on them. The official recommendation is to self-host — or skip servers entirely: fully offline plugins exist for everything.

See STT plugins for fully offline engines if you'd rather not depend on any server.

urls semantics:

  • List, tried in order until one succeeds: if you list more than one server, the plugin tries each in turn (each attempt bounded by timeout, default 5 seconds) and returns the first successful transcription. It does not race them in parallel.
  • timeout is per-attempt, in seconds, not a total budget across the whole list.
  • A request that exhausts every URL without success raises no exception from the plugin call itself. The caller (the listener, below) just gets no transcript back.

Listener-side fallback: a second STT engine, separate from the URL retry

Independent of this plugin's own multi-URL retry, ovos-dinkum-listener supports a completely separate fallback STT engine. This is a different plugin entirely, used only if the primary engine returns no utterance. Configure it under the stt section:

{
  "stt": {
    "module": "ovos-stt-plugin-fasterwhisper",  // primary, offline
    "fallback_module": "ovos-stt-plugin-server", // used only if the primary returns nothing
    "ovos-stt-plugin-server": {
      "urls": ["http://localhost:8080/stt"]
    }
  }
}

This is also useful the other way around: a fast, light primary engine locally, with a heavier server-backed engine as the fallback for when the light one comes back empty.


Docker Deployment

Create a Dockerfile

FROM python:3.11-slim
RUN pip install --pre "ovos-stt-http-server>=0.25.1a3"
RUN pip install {YOUR_STT_PLUGIN}
ENTRYPOINT ["ovos-stt-server", "--engine", "{YOUR_STT_PLUGIN}"]

The console script is ovos-stt-server (not ovos-stt-http-server, which is the PyPI package name).

Build & Run

docker build -t my-ovos-stt .
docker run -p 8080:8080 my-ovos-stt

Pre-built containers are also available via the ovos-docker-stt repository.


Tips & Caveats

  • /stt takes raw audio bytes, not a multipart upload. Send the PCM/WAV bytes as the request body (curl --data-binary @audio.wav). Pass sample_rate/sample_width as query params if they differ from the 16000/2 defaults. Those defaults are assumed when reading the raw body.

  • Audio Formats: the native /stt endpoint takes raw PCM/WAV bytes. The vendor-compatible multipart routers (OpenAI, ElevenLabs, Groq, and the rest) accept other container formats only when pydub is installed, which the base install omits: pip install --pre 'ovos-stt-http-server[audio]>=0.25.1a3'. Without that extra, uploading anything other than WAV through those routers returns HTTP 501 naming the missing format support.

  • Securing Endpoints: Consider putting a reverse proxy (NGINX, Traefik) in front for SSL or API keys. Minimal NGINX server block, proxying plain HTTP to the server on port 8080:

server {
    listen 80;
    server_name stt.example.lan;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
    }
}

Add TLS yourself, for example with certbot --nginx (see the Certbot documentation). See also tts-server: reverse proxy for the TTS side.

  • Plugin Dependencies: Some STT engines require heavy native libraries. Bake them into your Docker image.


Read next: TTS Server Related: Translate Server · Server Compatibility Layers · STT Plugins · Privacy & Security