API reference
https://api.suisse-speech.ch/v1 · X-API-KeyGenerated from the OpenAPI 3.0.3 description of Suisse Speech API 1.5.0. Download it as openapi.json to generate a client or import it into your API tool.
About this API
Speech-to-text and text-to-speech for Swiss German, its regional dialects and other languages. Both directions are available in two modes:
- batch — send a whole file or a whole text, get the whole result
- realtime — stream over a WebSocket and receive results as they are produced
Start with GET /capabilities: it lists every voice, language, dialect,
audio format, parameter range and error code this deployment accepts, so a
client never has to hard-code them.
All endpoints require an X-API-Key header with a valid sv_* API key.
The environment travels with the key: sv_… is live, sv_test_… is the
sandbox — not billed, no allowance consumed, its own budget and concurrency
slice. A sandbox key always receives structurally identical synthetic
audio and transcripts without any engine being called: free, instant and
deterministic, for continuous integration. Real voices and recognition need
a live key. The header X-Suisse-Sandbox-Mode: synthetic is still accepted
but no longer needed. Every
response reports which environment answered in X-Suisse-Environment.
WebSocket endpoints additionally accept
Sec-WebSocket-Protocol: suisse.v1, key.<api key> for browser clients.
Limits apply per API key: 600 requests/minute with a burst limit of 25
requests/second, a ceiling on simultaneous requests, and a daily budget of
audio seconds. An evaluation key additionally carries a fixed allowance of audio
hours. See limits and usage_today in GET /capabilities, and the remaining
allowance in GET /usage. Synthesis and recognition are both metered in audio
seconds, to the second, with no minimum per request.
Reading a 429. Four different things answer with status 429, and error.code
in the response body says which one it is:
*.rate_limited- too many requests in the window, or the daily audio budget is spent. Retry afterRetry-After.*.capacity- the service is momentarily busy. Retry afterRetry-After.*.plan_limit_reached- too many simultaneous requests on this key. Reduce parallelism; retrying at the same parallelism does not help.*.entitlement_exhausted- the evaluation allowance of audio hours is used up. Retrying does not help; contact us to extend it.
An HTTP client that reports only the status line hides this distinction, so log the response body of every 429.
Synthesised audio and transcripts are processed in memory and not stored;
identical texts are synthesised again each time. Every synthesis response
reports cache as miss (the field stays for compatibility).
Simultaneous work is admitted up to a published ceiling and refused beyond it
with 429 and a Retry-After header, rather than queued — a queue would
turn a capacity problem into a latency problem for the requests that did get
in. error.detail names which ceiling was reached: key_capacity (reduce
your own parallelism), service_capacity or host_load (back off and
retry). The current ceilings are in capacity in GET /capabilities, and
are what the deployment enforces today rather than a contractual
entitlement.
A narrative integration guide, including worked examples and client
behaviour for each error class, ships with the service as API_GUIDE.md.
/ttsSynthesise speech from text (batch)https://api.suisse-speech.ch/v1/tts
Send the complete text, receive the complete audio.
By default the response body is the audio itself, with the media type of
the requested format and the metadata in X-Suisse-* headers. Set
output: "json" (or send Accept: application/json) to receive a JSON
envelope with base64 audio instead.
Two speech models are available:
studio(default) — Our premier expressive engine. Natural prosody, nuanced delivery, and native support for regional Swiss German dialect translation and nuances. Recommended for production announcements, IVR greetings, audio content, and high-fidelity speech.swift— Low-latency engine optimized for real-time conversational streaming, telephone interactions, and instant responses.
language selects the language, dialect the regional variety within it. Swiss German (de-CH) is dialect-aware:
standard: supra-regional Swiss German (default).zurich: Zurich dialect (Züritüütsch).bern: Bernese dialect (Bärndütsch).basel: Basel dialect (Baaseldütsch).luzern: Lucerne dialect (Lozärnerdütsch). Both models render the text into the dialect - its own words and forms, not an accent - so the text may be written in Standard German.swiftdoes it in real time on the stream;studiodoes it with the highest fidelity and is the one to use for prepared audio. The compact formlanguage: "de-CH-zurich"is accepted as an alternative to the two fields.
Regional varieties and accents
The catalogue has two levels. language is a code that may carry a region, and a
regional standard variety is its own code: it-CH, fr-CA, en-GB, en-IN,
de-AT. dialect is a variety inside one language: de-CH + stgallen,
fr-CH + geneve, en-GB + scottish. The compact form <language>-<dialect>
(de-CH-stgallen) is accepted wherever a language code is, by recognition too.
Switzerland: Swiss German in its regional dialects and as Swiss High German
(de-CH + hochdeutsch, spoken word for word), Swiss French with the accents of
the Romandie, and Swiss Italian (it-CH). Swiss German dialects are rendered into
the dialect; every other variety keeps the words exactly as written and changes
only pronunciation, melody and rhythm.
GET /capabilities is the list. Every language and dialect there carries a
status: verified is measured in production, supported is rendered on a
best-effort basis and no quality figure is published for it.
Following the language of a conversation
Recognition reports the language it detected in lang, in the same codes this
endpoint accepts as language, the compact dialect form included. A client that
lists its target locales as recognition candidates (lang = de-CH-zurich,fr-CH,en)
receives exactly one of them back and can pass it here unchanged. No mapping table
and no server-side session is involved: every request stands on its own. The reply
text may be written in Standard German; for de-CH it is spoken in the chosen dialect.
For High German use de-CH-hochdeutsch (Swiss High German) or de.
style is a free-text delivery prompt (up to 600 characters) — e.g. "freundlich und herzlich", "ruhig und geduldig, kurze Pausen nach jedem Satz", or "professionell und sachlich". It steers how the text is spoken, never what is spoken: the supplied text is always rendered verbatim.
A text value whose first element is <speak> is parsed as markup.
Each construct is realised for real rather than passed on as a hint:
| element | effect |
|---|---|
<break time="500ms"/> |
silence of exactly that length in the audio |
<say-as interpret-as="digits|telephone|characters|ordinal|date|time|currency|number"> |
rewrites the text into its spoken form |
<sub alias="Aktiengesellschaft">AG</sub> |
speaks the alias |
<prosody rate pitch volume> |
real rate, pitch and gain changes on that passage |
<emphasis level="strong"> |
delivery directive on that passage |
<lang xml:lang="de-CH-bern"> |
switches language and dialect mid-text |
<mark name="x"/> |
returns the offset of that point in marks |
<p> <s> |
paragraph and sentence pauses |
say-as with digits or telephone speaks Swiss numbers the way our
phone agents do: dialect digit words and 3-3-2-2 grouping,
so +41 31 533 22 02 in Bernese becomes
"null drüü eis, füf drüü drüü, zwöi zwöi, null zwöi".
Elements that are not supported are rejected by name with an
explanation in error.detail, never silently ignored. <audio> and
<phoneme> are refused deliberately; use <sub alias> to respell a word.
Call GET /capabilities for the authoritative list.
Billed in produced audio seconds, measured to the second, with no minimum per request.
Request body · application/json · SynthesisRequest
| Field | Type | Description |
|---|---|---|
model | "studio" | "swift" | Speech synthesis model:
default "studio" |
textrequired | string | The text to speak. Rendered verbatim; never interpreted as an instruction. max 8000 chars |
voice | string | Voice id from default "anna" |
language | string | Language code. default "de-CH" |
dialect | string | Variety within the language.
default "standard" |
speed | number | Speaking rate multiplier (0.5 = half speed, 1.0 = normal pace, 2.0 = double speed). default 1 · min 0.5 · max 2 |
style | string | Free-text delivery prompt (up to 600 chars) steering tone, emotion, and pacing. Examples:
max 600 chars |
format | "wav" | "mp3" | "opus" | "pcm16" | "mulaw" | "alaw" | default "mp3" |
sample_rate_hz | integer | Output sample rate. Permitted values depend on |
output | "audio" | "json" |
default "audio" |
Responses
| Status | Description |
|---|---|
200 | Synthesised audio. Body is the raw audio unless Schema: file |
400 | Invalid parameters (unknown voice, language, dialect, format or speed) Schema: Error |
401 | Authentication required Schema: Error |
413 | Text exceeds the length limit Schema: Error |
429 | Rate limit, daily audio-seconds budget, or concurrency ceiling.
Schema: Error |
503 | Service temporarily unavailable Schema: Error |
504 | Request timed out Schema: Error |
/tts/streamSynthesise speech from text (realtime, WebSocket)wss://api.suisse-speech.ch/v1/tts/stream
Open a socket, push text as it becomes available, receive audio while the rest of the text is still being written. Lower time-to-first-audio than batch, and the right mode when the text comes from a live source.
Authenticate with the X-API-Key request header, or — from a browser,
which cannot set handshake headers — with the subprotocol field:
Sec-WebSocket-Protocol: suisse.v1, key.<api key>.
{"type":"session.start","model":"swift","voice":"anna","language":"de-CH","dialect":"zurich",
"speed":1.0,"style":"freundlich","format":"pcm16","sample_rate_hz":24000}
{"type":"text.append","text":"Grüezi. "}
{"type":"text.flush"}
{"type":"session.end"}
Add "transport":"binary" to session.start to receive audio as raw
binary WebSocket frames instead of base64 inside JSON — a third fewer
bytes and no decode step. Control frames stay JSON, so a binary frame is
always audio, in order, in the encoding session.ready names. The
default is json, so existing clients are unaffected.
Text is synthesised segment by segment at sentence boundaries. Send
text.flush to force a partial segment out without ending the session.
A text.append whose content is a complete <speak> document is expanded
into passages immediately, so markup works in this mode too.
{"type":"session.ready","request_id":"...","voice":"anna","language":"de-CH",
"dialect":"zurich","encoding":"pcm16","sample_rate_hz":24000,"frame_bytes":4096}
{"type":"audio.chunk","seq":1,"encoding":"pcm16","sample_rate_hz":24000,"data":"<base64>"}
{"type":"segment.done","characters":42,"duration_seconds":2.31,"cache":"miss"}
{"type":"mark","name":"after_ref","offset_seconds":7.15}
{"type":"session.complete","duration_seconds":7.94,"frames":48,"cache":{"hits":0,"misses":3}}
{"type":"error","error":{"code":"tts.unavailable","message":"...","request_id":"..."}}
Audio frames are a fixed frame_bytes in size apart from the final one.
Concatenate the decoded data values in seq order to reconstruct the
stream.
Responses
| Status | Description |
|---|---|
101 | Switching Protocols |
401 | Authentication required |
/tts/voicesList voices, languages and audio formatshttps://api.suisse-speech.ch/v1/tts/voices
The voice catalogue on its own, for clients that only need to populate a
picker. A subset of GET /capabilities.
Responses
| Status | Description |
|---|---|
200 | Voice catalogue Schema: object |
401 | Authentication required Schema: Error |
/tts/prewarmPrewarm phrases - stores nothing on this servicehttps://api.suisse-speech.ch/v1/tts/prewarm
On this service synthesised audio is processed in memory and not stored, so prewarming
stores nothing: every phrase comes back skipped, and nothing is synthesised or billed.
The endpoint stays for compatibility.
Needs a live key: a sandbox key never reaches the engine, so there would be nothing to warm.
Request body · application/json
| Field | Type | Description |
|---|---|---|
model | "studio" | "swift" | Speech model to prewarm (studio or swift). default "studio" |
phrasesrequired | string[] | The passages to synthesise ahead of time. |
voice | string | default "anna" |
language | string | default "de-CH" |
dialect | string | default "standard" |
speed | number | default 1 |
style | string |
Responses
| Status | Description |
|---|---|
200 | What was warmed Schema: object |
400 | No phrases, too many phrases, or a sandbox key |
429 | Rate limited or allowance exhausted |
/sttTranscribe an audio filehttps://api.suisse-speech.ch/v1/stt
Sync transcription for short audio (≤ 25 MB, ≤ 5 min). Pass audio as multipart/form-data
with field audio. Optional fields: lang (default de-CH), vocabulary
(JSON array of up to 200 strings to bias recognition).
Multilingual recognition
lang takes one language code, several candidates separated by commas
(de-CH,fr-CH,en), or auto (short for de-CH,fr-CH,it,en). With more than
one candidate the spoken language is detected automatically, the transcript is
written in that language, and the result reports it in lang. The reported
value is always one of the candidates given, and is accepted unchanged as
language by POST /tts and /tts/stream. Name only the languages you expect:
fewer candidates means a steadier result on short utterances.
A candidate may carry a region or a dialect (fr-CH, de-CH-zurich) and is
reported back exactly so, which lets the choice of dialect travel with the code.
Recognition reports the language, not the dialect that was spoken.
Swiss German dialects and Standard German are both recognised under de-CH
and are written in Standard German.
Request body · multipart/form-data
| Field | Type | Description |
|---|---|---|
audiorequired | file | PCM16 16kHz mono WAV is recommended. WAV / Opus / FLAC accepted. |
lang | string | One code, comma-separated candidates ( default "de-CH" |
vocabulary | string | Terms to bias recognition towards - names, places, product words. Either a JSON array of strings or a comma-separated list; at most 200 terms of 100 characters each. A value that is neither is refused with 400 rather than ignored. |
Responses
/stt/streamStreaming transcription (WebSocket)wss://api.suisse-speech.ch/v1/stt/stream
WebSocket endpoint at wss://api.suisse-speech.ch/api/v1/stt/stream. Upgrade with
the X-API-Key header (or ?api_key=sv_... query param).
{"type":"config","lang":"de-CH","vocabulary":["..."]} // first message
{"type":"audio.append","data":"<base64 PCM16 16kHz mono>"} // chunks
{"type":"audio.end"} // signal end
Binary frames are also accepted as raw PCM16 audio after config.
{"type":"session.ready","request_id":"..."}
{"type":"transcript.partial","text":"...","words":[...],"lang":"de-CH","duration_s":1.2}
{"type":"transcript.final","text":"...","words":[...],"lang":"de-CH","duration_s":3.4}
{"type":"session.complete","request_id":"..."}
{"type":"error","code":"stt.upstream_unavailable","message":"...","request_id":"..."}
Partial events are emitted on a fixed 250 ms cadence.
Multilingual recognition
lang in config takes one code, comma-separated candidates
("de-CH,fr-CH,en") or "auto", exactly as on POST /stt. Every
transcript.partial and transcript.final then carries the language detected
for that stretch of speech in lang, so a change of language during a session
is visible as it happens.
Responses
| Status | Description |
|---|---|
101 | Switching Protocols |
401 | Authentication required |
/jobsList recent jobs for this keyhttps://api.suisse-speech.ch/v1/jobs
Parameters
| Name | Type | Description |
|---|---|---|
limit | integer · query |
Responses
| Status | Description |
|---|---|
200 | Jobs, newest first Schema: object |
/jobsSubmit an asynchronous synthesis jobhttps://api.suisse-speech.ch/v1/jobs
For text long enough that holding a connection open for the whole
synthesis is the wrong shape. Acknowledged in milliseconds; the audio is
collected later from /jobs/{id}/audio.
Everything is validated at submission, so a bad voice or malformed
markup is a 400 here rather than a failure discovered later.
Results are kept for 48 hours. Up to 200 jobs may be queued per key. Jobs queue behind interactive traffic and are metered identically.
Request body · application/json
Responses
/jobs/{id}Job status, and the result when it has finished/jobs/{id}/audioCollect the audio of a finished synthesis jobhttps://api.suisse-speech.ch/v1/jobs/{id}/audio
Parameters
| Name | Type | Description |
|---|---|---|
idrequired | string · path |
Responses
| Status | Description |
|---|---|
200 | The audio Schema: file |
400 | Not finished, not found, or the result has expired Schema: Error |
/capabilitiesDiscover everything the speech API acceptshttps://api.suisse-speech.ch/v1/capabilities
Runtime discovery. Returns every voice, language, dialect, audio format, parameter range and error code the service accepts, plus the calling key's own quota for the current day.
Read this instead of hard-coding values: a new voice or language appears here without any client change.
Responses
| Status | Description |
|---|---|
200 | Capability document Schema: Capabilities |
401 | Authentication required Schema: Error |
/usageWhat this key has consumedhttps://api.suisse-speech.ch/v1/usage
Read from the billing ledger, not from the rate limiter's expiring counters, so these are the same numbers an invoice is built from, in the same unit. A customer billed by the second is entitled to reconcile continuously rather than at month end.
Sandbox traffic is reported separately and is never billed.
Parameters
| Name | Type | Description |
|---|---|---|
from | string · query | Start of the window. Defaults to the start of the current month. |
to | string · query | End of the window. Defaults to now. |
granularity | "total" | "day" · query | Add |
Responses
| Status | Description |
|---|---|
200 | Consumption for the window Schema: object |
400 | Invalid window |
401 | Missing or invalid key |
Schemas
Word| Field | Type | Description |
|---|---|---|
wrequired | string | The word. |
startrequired | number | Start time in seconds from the audio start. |
endrequired | number | End time in seconds. |
confrequired | number | Confidence in [0,1]. min 0 · max 1 |
Transcript| Field | Type | Description |
|---|---|---|
textrequired | string | |
langrequired | string | Language of the transcript. When the request named several candidate languages (or e.g. "de-CH" |
wordsrequired | Word[] | |
duration_srequired | number | Audio duration in seconds. |
Error| Field | Type | Description |
|---|---|---|
errorrequired | object |
Voice| Field | Type | Description |
|---|---|---|
idrequired | string | Value to pass as e.g. "anna" |
namerequired | string | e.g. "Anna" |
genderrequired | "female" | "male" | "neutral" | |
timbrerequired | string | e.g. "warm" |
pacerequired | string | e.g. "medium" |
descriptionrequired | string | |
best_forrequired | string[] | |
languagesrequired | string[] |
Dialect| Field | Type | Description |
|---|---|---|
idrequired | string | Value to pass as e.g. "zurich" |
namerequired | string | e.g. "Züridütsch" |
region | string | e.g. "Zürich" |
Language| Field | Type | Description |
|---|---|---|
coderequired | string | e.g. "de-CH" |
namerequired | string | e.g. "Swiss German" |
statusrequired | "verified" | "supported" |
|
dialectsrequired | Dialect[] | |
default_dialect | string | e.g. "standard" |
AudioFormat| Field | Type | Description |
|---|---|---|
idrequired | "wav" | "mp3" | "opus" | "pcm16" | "mulaw" | "alaw" | |
mimerequired | string | e.g. "audio/mpeg" |
extensionrequired | string | e.g. "mp3" |
description | string | |
sample_rates_hzrequired | integer[] | |
default_sample_rate_hzrequired | integer | e.g. 24000 |
streamablerequired | boolean |
SynthesisRequest| Field | Type | Description |
|---|---|---|
model | "studio" | "swift" | Speech synthesis model:
default "studio" |
textrequired | string | The text to speak. Rendered verbatim; never interpreted as an instruction. max 8000 chars |
voice | string | Voice id from default "anna" |
language | string | Language code. default "de-CH" |
dialect | string | Variety within the language.
default "standard" |
speed | number | Speaking rate multiplier (0.5 = half speed, 1.0 = normal pace, 2.0 = double speed). default 1 · min 0.5 · max 2 |
style | string | Free-text delivery prompt (up to 600 chars) steering tone, emotion, and pacing. Examples:
max 600 chars |
format | "wav" | "mp3" | "opus" | "pcm16" | "mulaw" | "alaw" | default "mp3" |
sample_rate_hz | integer | Output sample rate. Permitted values depend on |
output | "audio" | "json" |
default "audio" |
SynthesisResult| Field | Type | Description |
|---|---|---|
request_idrequired | string | |
voicerequired | string | e.g. "anna" |
languagerequired | string | e.g. "de-CH" |
dialect | string | e.g. "zurich" |
speed | number | e.g. 1 |
charactersrequired | integer | Characters of spoken text, after markup has been expanded. |
passages | integer | Number of separately rendered passages. 1 for plain text. |
cache | "hit" | "partial" | "miss" | Always |
marks | object[] | Present when the document contained elements. |
audiorequired | object |
Job| Field | Type | Description |
|---|---|---|
idrequired | string | e.g. "job_50f665b6efff3fbfb6b58fdf" |
kindrequired | "tts" | |
statusrequired | "queued" | "running" | "succeeded" | "failed" | |
environment | "live" | "sandbox" | |
created_atrequired | string | |
started_at | string | |
finished_at | string | |
audio_seconds | number | |
result_expires_at | string | |
audio | object | Present once the job has succeeded. |
callback | object | Delivery state, when the job carried a callback_url. |
transcript | object | Present on a finished recognition job. |
error | object |
CapabilitiesRuntime description of the whole service. Generated from the same catalogues the request handlers validate against, so it cannot drift from the implementation.
| Field | Type | Description |
|---|---|---|
api | object | |
authentication | object | |
text_to_speech | object | |
speech_to_text | object | |
limits | object | |
capacity | object | Concurrency ceilings this deployment enforces, and how much is in flight. |
cache | object | Synthesis cache counters. The cache is switched off on this service: no audio is stored. |
usage_today | object | |
errors | object[] |