HTTP API

Talkies serves HTTP on port 8000. When TALKIES_AUTH_TOKEN is set, send Authorization: Bearer <token> on every request except /healthz.

Discovery and health

Route Result
GET /healthz {"ok": true, "device": "...", "models": ["..."]}
GET /v1/models OpenAI-style enabled models with modality and max_concurrency
GET /v1/audio/voices {"voices": [...]} with voice, model, default, and optional origin

Voice names are scoped to their model. For the Qwen3 base voice-cloning models and Chatterbox Turbo, origin marks a voice as builtin (shipped in the image or checkpoint) or custom (found under /data/custom-voices).

Transcription

POST /v1/audio/transcriptions accepts multipart form data. Supply exactly one of file or file_path.

Field Required Default Behaviour
file one of file/path none Multipart audio upload, limited by TALKIES_MAX_UPLOAD_BYTES
file_path one of file/path none Relative staged path or http(s) URL fetched and cached by the server
model yes none Enabled ASR slug; unknown returns 404
language no registry default Source-language hint
response_format no json json, text, verbose_json, srt, or vtt
diarization no false true splits stereo left/right channels into speakers L and R
timestamp_granularities[] no empty Accepted for compatibility; verbose JSON emits available segments and words
prompt, temperature no none Accepted for OpenAI compatibility and ignored
curl -s http://127.0.0.1:8000/v1/audio/transcriptions \
  -F "file=@lecture.mp3" \
  -F "model=whisper-large-v3-turbo" \
  -F "response_format=verbose_json"
Format Response
json {"text": "..."}
text text/plain transcript
verbose_json JSON with task, language, duration, text, segments, and words
srt application/x-subrip subtitles
vtt text/vtt subtitles

Verbose JSON always contains segments and words; an executor without alignment data returns empty arrays. Stereo diarization only accepts 2-channel audio. It transcribes each channel independently, merges the timelines, and marks segments and words with channel: "L" or channel: "R".

For audio longer than the configured threshold, Talkies normalizes audio and uses VAD segmentation before calling the ASR backend. See Configuration.

The four bundled English Sherpa-ONNX variants and bundled Vosk small English are valid model values here as well as on the live WebSocket route. Their file requests are decoded through a short-lived native stream after normalization; see Streaming for the engine-specific behavior and Models for selectable slugs.

If the model has no free inference slot, transcription and speech requests return HTTP 429. A request for a different model while inference is active returns HTTP 409 rather than evicting the active backend. Every status is listed under Errors.

Speech

POST /v1/audio/speech accepts a JSON body. It returns encoded audio bytes or, for Qwen3 with response_format: "pcm", an incremental raw PCM response.

Field Required Default Rules
model yes none Enabled TTS slug
input yes none Non-empty source text
voice no model default Must exist in that model’s voice catalog
response_format no mp3 mp3, opus, aac, flac, wav, or pcm
speed no 1.0 0.25 to 4.0; backend support varies
instructions no none Qwen3 mode-specific instruction/voice text; Kokoro ignores it
language no model default Qwen3 language label
temperature no backend default 0 to 2, Qwen3 sampling option
top_k no backend default 1 to 1000, Qwen3 sampling option
top_p no backend default 0 to 1, Qwen3 sampling option
repetition_penalty no backend default 0.5 to 2, Qwen3 sampling option
max_new_tokens no backend default 1 to 2048, Qwen3 sampling option
do_sample no backend default Qwen3 sampling option
curl -s http://127.0.0.1:8000/v1/audio/speech \
  -H 'Content-Type: application/json' \
  -d '{"model":"kokoro-82m","input":"Hello.","voice":"af_heart","response_format":"wav"}' \
  --output hello.wav

pcm is headerless signed 16-bit little-endian mono audio. All formats except Qwen3 PCM are generated as a complete response. Qwen3 PCM sends Content-Type: application/octet-stream and X-Sample-Rate: 24000; see Qwen3 streaming TTS.

File staging

The staging area is under $TALKIES_DATA_DIR/files. Leading slashes are normalized away; traversal segments, backslashes, null bytes, and paths whose resolved target escapes the staging root are rejected.

Route Behaviour
GET /v1/files List staged files with paths, sizes, and modification times
PUT /v1/files/{path} Store raw request bytes; returns 201 with path and size
GET /v1/files/{path} Return a staged file
DELETE /v1/files/{path} Delete a staged file and prune empty parents
curl --upload-file clip.wav http://127.0.0.1:8000/v1/files/jobs/clip.wav
curl -s http://127.0.0.1:8000/v1/audio/transcriptions \
  -F 'file_path=jobs/clip.wav' \
  -F 'model=whisper-large-v3-turbo'

Staging is shared by all callers of an instance. It is not a per-user store; follow the data-retention guidance in Operations.

Model lifecycle

Route Behaviour
GET /api/ps Loaded models with id, repo, loaded, idle_seconds, active_requests, and max_concurrency
DELETE /api/ps/{model_id} Unload one loaded model; 404 if unknown or not loaded
POST /unload Unload every loaded model; returns {"unloaded": [...]}

Any active inference request pins its model: HTTP or MCP transcription, speech, or a live ASR stream. DELETE /api/ps/{model_id} returns HTTP 409 while the target model has active requests, and POST /unload returns 409 while any model has them. A request that needs a different model also gets 409 until the pin is released.

Errors

Error responses are JSON with a detail field: a message string, or a list of field errors for 422.

Status When
400 Both or neither of file and file_path; a TTS slug on the transcription route or an ASR slug on the speech route; audio ffmpeg cannot decode; diarization on audio that is not 2-channel; an invalid staged path or a refused URL download; an unknown voice, an unsupported response_format, or empty input; a backend rejecting the request, such as a Chatterbox reference clip of 5 seconds or less
401 TALKIES_AUTH_TOKEN is set and the bearer token is missing or wrong
404 Unknown model slug; a staged file_path or /v1/files/{path} that does not exist
409 Another model has active inference requests, or an unload targets a model in use
413 An upload larger than TALKIES_MAX_UPLOAD_BYTES
422 A request body that fails field validation, such as speed outside 0.25 to 4.0
429 The model’s max_concurrency is reached
500 ffmpeg failed to encode the speech output
503 A TTS backend cannot list its voices or find its model files

A Qwen3 PCM stream has already sent its headers when synthesis starts, so a failure during the stream ends the response early instead of returning a status.

MCP

Talkies mounts a stateless Streamable HTTP MCP server at /v1/mcp; both the bare path and the trailing-slash form work. It exposes six tools:

Tool Arguments Returns
list_models none ASR models with slug, executor, default_source_lang, default_target_lang, default_task, and loaded
transcribe file_path, model, optional language, response_format (default json), diarization (default false) The transcript as a string; JSON formats are JSON-encoded
list_files none The same listing as GET /v1/files
put_file path, content_base64 {"path": ..., "size": ...}
get_file path path, size, and content_base64
delete_file path {"deleted": ...}

transcribe takes an http(s) URL or a staged path, not an upload, and runs the same pipeline as the HTTP route. File bytes travel as base64 in JSON and are capped at TALKIES_MAX_UPLOAD_BYTES in both directions. A failed tool call returns a tool error with a readable message. The MCP server offers ASR and file tools only; use the HTTP speech route for TTS.

The HTTP routes live in src/talkies/server.py; the MCP tools live in src/talkies/mcp_server.py.