The service is running in test mode — beta testing is underway. Errors are possible.
Home/API documentation

API documentation for integrations

REST API for voiceover, streaming generation, the voice catalog, creating your own voice and transcription.

# Text-to-speech (voice — id from GET /api/v1/voices) curl https://hancvoice.com/api/v1/tts \ -H "X-API-Key: $HANCVOICE_KEY" \ -H "Content-Type: application/json" \ -d '{ "voice": "de_at_f1", "text": "Hallo! Das ist HancVoice.", "language": "de" }' --output out.wav
import fs from "node:fs/promises"; const response = await fetch("https://hancvoice.com/api/v1/tts", { method: "POST", headers: { "X-API-Key": process.env.HANCVOICE_KEY, "Content-Type": "application/json" }, body: JSON.stringify({ voice: "de_at_f1", text: "Hallo! Das ist HancVoice.", language: "de" }) }); const audio = Buffer.from(await response.arrayBuffer()); await fs.writeFile("out.wav", audio);
import os, requests response = requests.post( "https://hancvoice.com/api/v1/tts", headers={ "X-API-Key": os.environ["HANCVOICE_KEY"], "Content-Type": "application/json", }, json={ "voice": "de_at_f1", "text": "Hallo! Das ist HancVoice.", "language": "de", }, ) open("out.wav", "wb").write(response.content)
REST
integration interface
Engine
voiceover generation
318
voices in the catalog
WAV
API response format
Capabilities

Built for integration

Synchronous generation

A POST request returns a ready WAV file.

REST without an SDK

Examples for cURL, Node.js and Python over plain HTTP.

Streaming mode

A separate endpoint for streaming scenarios.

10 languages + Auto

One endpoint, the language parameter: Russian, English, German, French, Spanish, Italian, Portuguese, Chinese, Japanese and Korean.

System, custom and variants

Use the library, saved custom voices and sound variants.

EU hosting

Servers in EU, GDPR.

Reference

API v1 — endpoints

GET/api/v1/voicesVoice catalog: id, language, gender, style, license, sample
GET/api/v1/voices/{voice}/avatarVoice avatar (uploaded image or generated gradient)
GET/api/v1/languagesSupported languages: code, name, tier, voice count
POST/api/v1/ttsSpeech synthesis → WAV or MP3 (format parameter)
POST/api/v1/tts_streamStreaming synthesis (chunked PCM) for low latency — a clean stream without post-effects and without mp3
POST/api/v1/voices/createCreate a voice: name, audio_b64 (≤10 MB base64), ref_text (=audio word-for-word), consent="voice-data-consent,privacy", language? → voice id
GET/api/v1/voices/mineYour own voices (created clones + purchased): voice (slug), name, language, kind, sample_url — API key required
POST/api/v1/transcribeTranscribe a short fragment: audio_b64, language? (full name) → text. The duration counts toward the plan's monthly transcription-minutes limit
GET/api/v1/statusEngine readiness (real-time agents): ready, model_ready, engine_up, latency_ms
POST/api/v1/warmupWarm up the engine before a call session (without charging characters)

GET /api/v1/voices — filters: q (search by name/style), language (ru, en, de…), gender (male/female), limit (default 200, max 500), offset. Pass the voice field from the response to POST /tts.

# GET /api/v1/voices?language=ru&gender=male&limit=200 { "object": "list", "total": 152, "count": 152, "voices": [ { "voice": "de_at_f1", "name": "Greta", "language": "de-AT", "gender": "female", "style": "Young", "description": "Female voice — Austrian German, young, joyful, for presentations.", // in the response language (?locale=) "labels": { // filter on the *_key fields: they are stable and never translated "gender": "female", "language": "de-AT", "language_name": "Austrian German", "accent": "Austrian German", "age": "young", // age is "" unless the catalogue marks it "style": "Young", "style_key": "young", "use_case": "presentations", "use_case_key": "presentations", "emotion": "joyful", "emotion_key": "joyful" }, "license": "commercial", "badge": null, // null | "beta" | "soon" (soon = synthesis disabled) "sample_url": "https://hancvoice.com/api/v1/voices/de_at_f1/sample", // permanent link, redirects to the current file "avatar_url": "https://hancvoice.com/api/v1/voices/de_at_f1/avatar" } ] }

Avatars & languages. Avatar: 302 to the image or an SVG gradient — drop it straight into <img>. Languages: tier = main (ready) | beta | soon.

# GET /api/v1/languages — supported languages (code, name, tier, voice count) { "object": "list", "total": 24, "languages": [ { "code": "de", "name": "German", "native_name": "Deutsch", "tier": "main", "voice_count": 32 } ] } # GET /api/v1/voices/{voice}/avatar → 302 to image, or a generated SVG. Use directly in <img src>. <img src="https://hancvoice.com/api/v1/voices/de_at_f1/avatar" width="64" />
Access

Authentication

All endpoints require an API key: the X-API-Key: <key> header or Authorization: Bearer <key> (except GET /api/v1/voices, GET /api/v1/status, GET /api/v1/languages and GET /api/v1/voices/{voice}/avatar — open). The key is created in your account → the “API” section; the API is available from the Starter plan and up (otherwise 403). Synthesis charges characters against the key's plan — at a zero balance it returns 402. Rate limit is by plan: Starter — 30 requests/min, Pro — 120, Business — 300, Scale — 600 (fallback 60); exceeding it → 429, the Retry-After header.

POST /api/v1/tts

Parameters

voiceVoice ID from GET /api/v1/voices. ⚠️ If the field is missing, synthesis falls back to the default voice (de_at_n1) and characters are still charged — always pass the ID explicitly.
texttext to voice; max length depends on the key's plan (required)
languageUsually not needed: the language is detected from the text, and the voice reads the text in that language. A voice sounds native only in its verified_languages (GET /api/v1/voices); in other languages it keeps its accent, and the response says so: X-Synthesis-Language, X-Voice-Language, X-Language-Verified: false. The parameter counts only for a voice without a language of its own, when the text is too short to detect the language: de, en, ru, fr, es, it, pt, zh, ja, ko (also de-DE, en-US …) and full names (German, English …). Plus Auto: the engine detects the language from the text — the only way to synthesise a beta language with your own voice.
instructemotion/style in English, e.g. "calm, friendly" (opt.)
emotionstudio-voice emotion (for voices with recorded emotional references): joyful, excited, tender, calm, sad, serious, tense, angry, afraid, surprised (opt.; without the parameter — neutral)
saturationsaturation/harmonic “coloring” of the timbre (post-processing): {"preset":"tube"|"tape"|"air","intensity":0–1} (opt.)
speedspeech tempo 0.5–2.0 (opt.) Values outside the range are clamped to the nearest bound; the response then carries X-Effects-Failed: speed_clamped_to_….
gain_dbvolume −24…+24 dB (opt.)
eqtimbre equalizer: {"bands":[{"hz":80,"db":4}, …]} (opt.)
effectsound preset: radio, audiobook, podcast, phone, cinematic, megaphone, vintage, deep, bright, warm, narrator_warm, intimate, spacious, whisper, concert; characters: robot, cartoon, chipmunk, monster, villain, echo_hall; studio: studio_announcer, studio_natural, studio_rich. Premium effects (characters, studio, cinematic, concert, megaphone, vintage) — only on paid plans, otherwise ignored (opt.)
seeda number for a deterministic repeat of the same voiceover (opt.)
cleantrue — neural denoising (separate from effect, opt.)
backgroundbackground/ambience: rain, thunder, sea, forest, birds, wind, stream, night, street, cafe, crowd, fire (opt.)
bg_levelbackground volume 0.05–0.8 (opt., default 0.25)
lead_silence_mssilence at the start of the audio, ms (opt., for agents; default 0)
format"wav" (default) or "mp3" (opt.)
response_formatTelephony output (B2B/IVR): g711_alaw_8k, g711_ulaw_8k, g722_16k, opus, pcm_s16le_24k. The response is the codec stream + X-Audio-Format header (opt.)
containerwith response_format only: wav (container, default) or raw (raw frames for SIP/RTP) (opt.)

The response is a binary audio file: audio/wav or audio/mpeg (if format=mp3); for a telephony response_format, the corresponding codec container (see X-Audio-Format).

POST /api/v1/tts_stream

Streaming synthesis

Accepts voice, text, language, instruct, speed, gain_db, eq, effect, background, bg_level, saturation, seed and segments (up to 40 items: text, instruct up to 200 chars, silence 0–3000 ms). Response: raw PCM — 24 kHz, 16-bit, mono, little-endian (application/octet-stream, chunked); there is no WAV header, set the sample rate yourself.

# POST /api/v1/tts_stream → PCM stream (application/octet-stream, chunked) { "voice": "de_at_f1", "text": "Hallo! Das ist Streaming-Synthese.", "language": "de" }
POST /api/v1/voices/create

Creating a voice

namevoice name (required)
audio_b64sample recording in base64, ≤10 MB (required)
ref_textTranscript of the recording — word for word, max 2000 characters (longer → 400 ref_text_too_long). If the text does not match the audio, the voice sounds garbled.
consentconsent string — pass exactly "voice-data-consent,privacy" (voice data, GDPR); without it — 400 (required)
languagesample language, full name (Russian, English…) or ISO code; default German (opt.)

Pass the voice field from the response to POST /tts. The voice value below is an illustrative example; the real identifier comes in the response. Creating your own voice is available from a paid plan; the number of your own voices is unlimited — creating one costs 25.000 characters from your balance (storing and using it is free).

# Response — 200 OK (voice value is an example) { "ok": true, "voice": "usr_a1b2c3", "name": "My voice", "language": "German" }
GET /api/v1/voices/mine

Your own voices

GET /api/v1/voices returns only the public catalog. To get YOUR OWN voices — both those created via the API and those generated in your account/studio — authenticate with your API key and call this endpoint. It returns your own clones (kind="own") and voices purchased on the Marketplace (kind="marketplace"). Pass the voice field (slug) to POST /api/v1/tts.

# GET /api/v1/voices/mine — your own voices (API key required) curl https://hancvoice.com/api/v1/voices/mine \ -H "X-API-Key: $HANCVOICE_KEY" # Response — 200 OK { "object": "list", "count": 1, "voices": [ { "voice": "usr_a1b2c3", "name": "My voice", "language": "German", "gender": null, "kind": "own", "sample_url": "https://hancvoice.com/voice-samples/usr_a1b2c3.wav", "avatar_url": "https://hancvoice.com/generated-audio/voice-avatars/usr_a1b2c3.webp" } ] }

gender is null for own voices (not stored); sample_url may be null if no sample exists. When synthesizing with your own voice, the language is taken from the voice and overrides the language parameter in POST /tts.

Complete copy-paste flow — create, list, synthesize (replace the example key with your own from Account → API):

# Full own-voice flow — create → list (mine) → synthesize. # Example key below — replace it with your own key from Account → API. KEY="sk-live-9f3c1a7b4e2d8c6f0a5b3e1d7c9f2a4b6d8e0c1a3f5b7d9e" # 1) Create your voice (skip if you already have one in your account) curl -X POST https://hancvoice.com/api/v1/voices/create \ -H "X-API-Key: $KEY" -H "Content-Type: application/json" \ -d '{"name":"My voice","ref_text":"exact transcript of the recording","audio_b64":"<24kHz sample, base64>","consent":"voice-data-consent,privacy"}' # → {"ok":true,"voice":"usr_a1b2c3","name":"My voice","language":"German"} # 2) List your own voices → take the "voice" (slug) curl https://hancvoice.com/api/v1/voices/mine -H "X-API-Key: $KEY" # 3) Synthesize with your voice curl -X POST https://hancvoice.com/api/v1/tts \ -H "X-API-Key: $KEY" -H "Content-Type: application/json" \ -d '{"voice":"usr_a1b2c3","text":"Hallo, das ist meine eigene Stimme.","language":"de"}' --output out.wav
POST /api/v1/transcribe

Transcription

audio_b64audio in base64; intended for a short fragment (required)
languagethe FULL language name only: Russian, English, German, French, Italian, Spanish, Portuguese, Chinese, Japanese, Korean — plus 14 beta languages (Bulgarian, Czech, Greek, Ukrainian, Polish and others) as a recognition hint, and the literal "auto" (ISO codes are NOT accepted here, unlike /tts; without the parameter — auto-detect) (opt.)

Characters are charged per plan (≈1 min of audio = 1000 characters); the plan's monthly transcription-minutes limit applies.

# Response — language = vom Engine erkannter ISO-Code (z. B. de, en, ru) { "text": "Transkribierter Text des Fragments.", "language": "de" }
GET /api/v1/status · POST /api/v1/warmup

Status and warm-up

GET /api/v1/status (no key) — engine readiness for real-time. POST /api/v1/warmup (with a key; charges NO characters) warms up the engine before a series of calls.

# GET /api/v1/status { "ready": true, "model_ready": true, "engine_up": true, "latency_ms": 42 }
# POST /api/v1/warmup { "ok": true, "warmed": true, "warm_ms": 318 }
Response

Headers and the code field

Content-Typeaudio/wav or audio/mpeg (per the format parameter); for /tts_stream — application/octet-stream
X-Effects-Failedeffects that did NOT apply (on the engine or trimmed by the plan), comma-separated — the audio comes back without them
X-CacheHIT / MISS — whether the response came from cache. Note: with an API key a cache HIT is billed like a MISS (the cache saves generation time, not characters).
Retry-Afteron 429 — in how many seconds to retry the request

The code field in the error body (machine-readable): plan_required — a higher plan is needed; voice_limit — the plan's voice limit; plan_quota — the monthly transcription-minutes limit is used up; email_unverified — verify your email; voice_conflict — the voice name is already taken (when creating a voice).

Response codes

Errors

200Success — the response body contains the audio file
400Broken JSON or empty text. An unknown language returns the engine's 4xx (e.g. 400) with code engine_bad_request — the same on both /v1/tts and /v1/tts_stream
401Missing or invalid API key (X-API-Key / Authorization header)
402Not enough characters on the key's balance — top up the plan
403The feature is unavailable on the current plan (e.g. API — from Starter, custom voice — from a paid plan) or no access to the voice
409Conflict (voice_conflict): a voice with this name already exists — choose another name when creating a voice
413File too large: for a custom voice the audio is >10 MB (base64)
429Rate limit exceeded (per plan: Starter 30, Pro 120, Business 300, Scale 600 per minute; /voices/create 5/min, /warmup 20/min). Honour the Retry-After header. Code: rate_limited. Separately: concurrency_limit (also 429) means too many requests running at the same time on one account (per plan: Starter 2, Pro 3, Business 4, Scale 6 syntheses in parallel) — retry as soon as one finishes. No Retry-After, because the wait depends on your own in-flight request.
500Internal error (e.g. the database is unavailable on GET /v1/voices) — retry later.
502The synthesis/transcription engine did not respond (Bad Gateway) — wait briefly and retry
503The engine is asleep/unavailable or the service is overloaded — retry in a few seconds

The error body is JSON { "error": "description", "code": "machine-readable" }. Possible code values: auth_required, plan_required, rate_limited, concurrency_limit, no_credits, voice_create_credits, bad_request, empty_text, text_too_long, ref_text_too_long, name_too_long, voice_limit, voice_conflict, voice_access, plan_quota, no_audio, engine_busy, engine_unavailable, engine_error, billing_unavailable, temporarily_unavailable. On failure no characters are charged — except when the client aborts a stream (the delivered audio is billed pro rata).

All error codes

Full list of machine codes in the code field: auth_required (401) · plan_required (403, API from Starter) · text_too_long, bad_request, name_required, ref_text_required, ref_text_too_long, name_too_long, audio_b64_required, consent_required, voice_required (400) · voice_access · voice_not_found (404) · no_credits (402) · body_too_large, audio_too_large (413) · concurrency_limit (429, concurrency) · rate_limited (429, with Retry-After) · engine_bad_request (engine 4xx) · engine_error, engine_unavailable, engine_not_configured, temporarily_unavailable, mp3_failed, telephony_failed, transcribe_failed (5xx) · client_abort (499, the client dropped the connection).

Fine print

Practical details that are easy to miss: the request body is capped at 2 MB for /v1/tts and /v1/tts_stream (413 otherwise) and at 30 MB base64 for /v1/transcribe · speaker is a synonym for voice · the pause marker ‖ (U+2016) placed in the text yields exactly 400 ms of silence · instruct is silently trimmed to 500 characters and lead_silence_ms is capped at 5000 · with no seed the value 4242 is used, so output is reproducible by default (change the seed yourself if you want variation) · with no language German is assumed · GET /v1/languages accepts ?locale · the rate limit is counted per endpoint separately; own limits: /voices/create 5/min, /warmup 20/min, /voices/mine 60/min, open GET endpoints 120/min per IP · POST /v1/voices/clone is an alias of /voices/create.

Catalog

Effects and backgrounds

The effect parameter — one preset per request. Free ones are available to everyone; premium — on paid plans.

Delivery (free)radio — dry broadcast · audiobook — audiobook · narrator_warm — warm narrator · podcast — podcast · phone — phone · intimate — close · spacious — spacious · whisper — whisper · deep — lower · bright — brighter · warm — warmer
Studio (premium)studio_announcer — announcer broadcast · studio_natural — natural studio · studio_rich — rich “expensive” · cinematic — cinema · concert — hall · megaphone — megaphone · vintage — vintage
Characters (premium)robot — robot · cartoon — cartoonish · chipmunk — chipmunk · monster — monster · villain — villain · echo_hall — echo hall
Backgrounds (background)Nature: rain (rain) · thunder (thunderstorm) · sea (sea) · forest (forest) · birds (birds) · wind (wind) · stream (stream) · night (night). City: street (street) · cafe (cafe) · crowd (crowd). Cozy: fire (fireplace). Volume — bg_level 0.05–0.8
eq (timbre)8 bands: 80, 160, 300, 600, 1500, 3500, 6500, 10500 Hz, each −12…+12 dB. Example: {"bands":[{"hz":80,"db":-4},{"hz":3500,"db":3}]} — remove rumble, add clarity
Quotas

Limits

Request rateper the key's plan: Starter — 30/min, Pro — 120/min, Business — 300/min, Scale — 600/min. Endpoints with their own hard cap, regardless of plan: POST /v1/voices/create — 5/min, POST /v1/warmup — 20/min, GET /v1/status — 60/min per IP, GET /v1/voices — 120/min per IP.
Text lengthmax characters per POST /api/v1/tts depends on the key's plan
Character balancesynthesis charges characters against the key's plan; at a zero balance — 402
Transcription≈1 min of audio = 1000 characters; the plan's monthly minutes apply: Pro 100, Business 300, Scale 800 min. Free and Starter include NO transcription — the API answers 403 (plan_required). Quota exhausted → 403 (plan_quota).
File sizevoice creation: sample ≤10 MB (base64), otherwise 413
Example

Full request

POST /api/v1/tts { "voice": "cml_de", "text": "Heute ist das Wetter wunderbar.", "language": "de", "instruct": "calm, friendly", "speed": 1.0, "effect": "podcast", "eq": { "bands": [ { "hz": 3500, "db": 3 } ] }, "background": "rain", "bg_level": 0.2, "format": "mp3" } → 200 OK, Content-Type: audio/mpeg (binary file)

Get a key in a minute

Get an API key →
POST /api/v1/ttsGET /api/v1/voicesAPI SEO page