FonadaLabs
Login / Sign Up

Klone V2 Flash

Voice Clone · Fast

Klone V2 Flash is our faster voice-cloning model. It is a drop-in option on the same one-shot endpoint as Klone V2 Pro: add model=klone_v2_flash to the request and everything else stays the same. Flash can also stream audio over HTTPS or a WebSocket while it is being generated.

Quick start

  1. 1

    Get an API key

    Create a key in the Console and replace YOUR_API_KEY in the examples below.

  2. 2

    Install the SDK

    pip install fonadalabs
  3. 3

    Make your first request

    Generate speech in a Voice Arena voice with the Flash model. Replace YOUR_SHARE_ID with the voice's share ID.

    from fonadalabs.tts.client import TTSClient
    
    client = TTSClient(api_key="YOUR_API_KEY")
    
    audio_bytes = client.generate_audio(
        text="Hello from flash cloning.",
        language="Hindi",
        model="klone_v2_flash",
        share_id="YOUR_SHARE_ID",
        output_file="flash.wav",
    )

Overview

Key characteristics

  • Synchronous: the audio comes back inline in the HTTP response. No job ID and no polling.
  • 12 languages: Bengali, English, Gujarati, Hindi, Kannada, Malayalam, Marathi, Odia, Punjabi, Tamil, Telugu, Urdu.
  • Up to 5,000 characters per request. The server splits and merges long text.
  • Three delivery modes: a whole clip (/chunks), a streamed response (/stream), or a WebSocket with many utterances per connection (/ws).
  • /chunks does not use speed, duration, instruct or num_step; see Voice settings.

Request

API endpoint

Base URL: https://api.fonada.ai
Endpoint: /v1/voice-clone/chunks
Method: POST
Content-Type: multipart/form-data (always, even with no file upload)
Authorization: Bearer YOUR_API_KEY

curl
curl -X POST "https://api.fonada.ai/v1/voice-clone/chunks" \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "model=klone_v2_flash" \ -F "share_id=YOUR_SHARE_ID" \ -F "text=यह एक छोटा सा फ़्लैश वॉइस क्लोन टेस्ट है।" \ -F "language=hi" \ -F "output_audio_codec=mp3" \ -o voice.mp3
JavaScript (fetch)
const form = new FormData() form.append('model', 'klone_v2_flash') form.append('share_id', 'YOUR_SHARE_ID') form.append('text', 'यह एक छोटा सा फ़्लैश वॉइस क्लोन टेस्ट है।') form.append('language', 'hi') form.append('output_audio_codec', 'mp3') // optional, default wav // Do not set Content-Type yourself - the browser adds the multipart boundary. const res = await fetch('https://api.fonada.ai/v1/voice-clone/chunks', { method: 'POST', headers: { Authorization: 'Bearer YOUR_API_KEY' }, body: form, }) if (!res.ok) { const err = await res.json() // { detail: { error, message, details? } } if (err?.detail?.error === 'rate_limit_exceeded') { const wait = err.detail.details?.retry_after_seconds ?? 5 // wait `wait` seconds, then retry } throw new Error(err?.detail?.message || 'voice clone failed') } const url = URL.createObjectURL(await res.blob()) audioElement.src = url

The body of a successful response is the raw audio. Read it as a Blob or ArrayBuffer, not JSON. Response headers: Content-Type, Content-Disposition, X-Output-Codec, X-Upstream-Time-Ms, X-Processing-Time-Ms. No credit or model headers are returned.

Parameters (multipart form)

FieldRequiredDescription
textyesText to synthesize. Non-empty, at most 5,000 characters.
languageyes (default hi)ISO code or display name. See Languages.
modelyes for FlashMust be klone_v2_flash.
output_audio_codecno (wav)See Output formats.
share_idone of 3Catalog voice ID. Works on all tiers. audio, audio_url and audio_text are ignored when it is set.
audioone of 3Reference upload (WAV/MP3/M4A/FLAC/OGG/WebM), max 10 MB and 30 s. Enterprise only.
audio_urlone of 3Public HTTPS URL of a reference clip. Enterprise only.
audio_textnoTranscript of the reference audio. Omit it to auto-transcribe. Ignored with share_id.
Send exactly one of share_id, audio or audio_url. Zero returns 400 missing_audio_source; more than one returns 400 ambiguous_audio_source./chunks ignores speed, duration, instruct and num_step for Flash. To use them, call /stream or the WebSocket.

Languages

Pass an ISO code or a display name (case-insensitive). Anything else returns 400 invalid_language.

CodeLanguage
bnBengali
enEnglish
guGujarati
hiHindi
knKannada
mlMalayalam
mrMarathi
orOdia
paPunjabi
taTamil
teTelugu
urUrdu

Output formats

output_audio_codecContent-TypeExtensionEncoding
wav (default)audio/wav.wavPCM WAV
mp3audio/mpeg.mp3MP3
opusaudio/ogg.opusOpus in Ogg
pcm (linear16)audio/pcm.pcmRaw headerless 16-bit PCM
mulaw (ulaw)audio/basic.ulawμ-law (telephony)
alawaudio/x-alaw-basic.alawA-law (telephony)

For web playback, mp3 or opus give the smallest payload, and wav gives the lowest latency. pcm, mulaw and alaw are headerless raw streams, so your player needs to be told the format. An unsupported value returns 400 invalid_output_audio_codec.

Streaming and WebSocket

Klone V2 Flash can also send audio while it is being generated, so playback starts before the whole clip is ready. All three modes use the same API key, voices, languages, pricing and rate limit. Streaming and the WebSocket are Flash only; Klone V2 Pro uses /v1/voice-clone/chunks.

ModeEndpointBest for
One-shotPOST /v1/voice-clone/chunksGenerate a whole clip and get it back in one response.
HTTPS streamingPOST /v1/voice-clone/streamStart playback while the audio is still being generated (first audio in well under a second).
WebSocketwss://api.fonada.ai/v1/voice-clone/wsVoice agents and chat: keep one connection open and speak many utterances.

Which endpoint should I use?

You want to…Use
Save a file, or play a short clip after it is ready/chunks
Play audio as soon as possible in a web page, app or server/stream
Speak many replies in a row with the lowest overhead (voice agents, IVR, chat)/ws
Use instruct or num_step/stream or /ws (/chunks ignores them)

HTTPS streaming: POST /v1/voice-clone/stream

The response body is the audio itself, sent as it is generated. Text is split by sentence and generated in order, so playback can start on the first sentence while the rest is still being produced.

API endpoint

Base URL: https://api.fonada.ai
Endpoint: /v1/voice-clone/stream
Method: POST
Content-Type: multipart/form-data (or application/x-www-form-urlencoded when there is no file upload)
Authorization: Bearer YOUR_API_KEY
Model: Flash only. model may be omitted. Sending klone_v2_pro returns 400 streaming_not_supported; use /v1/voice-clone/chunks (also served as /v2/voice-clone/chunks) for Pro.

Parameters

Same fields as /chunks, plus the voice settings. Note that the default output is pcm here, not wav.

FieldRequiredDescription
textyesNon-empty, at most 5,000 characters.
languageno (hi)ISO code or display name. See Languages.
share_id / audio / audio_urlone of 3Reference voice, exactly as on /chunks (audio and audio_url are enterprise only).
audio_textnoTranscript of the reference clip; auto-transcribed when omitted (not available for Urdu).
output_audio_codecno (pcm)pcm, wav, mp3, opus, mulaw, alaw. See Streaming formats.
instructnoStyle hint, e.g. calm, friendly. Up to 500 characters (longer text is cut at 500).
num_stepnoDiffusion steps, 8 to 64 (advanced). More steps can improve quality and take longer.
speed, durationnoSee Voice settings. Currently unreliable; omit them unless you need them.
modelnoklone_v2_flash (default; the only streaming model).

Response

Status 200 with the audio as a chunked body, and these headers:

HeaderExampleMeaning
Content-Typeaudio/pcmMedia type of the chosen format.
X-Output-CodecpcmFormat of the body.
X-Sample-Rate240008000 for mulaw / alaw, otherwise 24000.
X-Channels1Always mono.
X-Credits-Charged13.65Credits charged for this request.

Errors found before any audio is sent come back as a normal JSON error (same format as /chunks) and the credits are refunded. If generation fails after audio has started, the stream simply ends early.

curl
curl -N -X POST "https://api.fonada.ai/v1/voice-clone/stream" \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "share_id=YOUR_SHARE_ID" \ -F "text=नमस्ते, आपका स्वागत है।" \ -F "language=hi" \ -F "output_audio_codec=mp3" \ -o stream.mp3

-N turns off cURL's buffering so bytes are written as they arrive. To play it live: ... -o - | ffplay -nodisp -autoexit -.

JavaScript (browser): play as it arrives
const form = new FormData() form.append('share_id', 'YOUR_SHARE_ID') form.append('text', 'नमस्ते, आपका स्वागत है।') form.append('language', 'hi') const res = await fetch('https://api.fonada.ai/v1/voice-clone/stream', { method: 'POST', headers: { Authorization: 'Bearer YOUR_API_KEY' }, body: form, }) if (!res.ok) throw new Error((await res.json()).detail.message) // The body is raw PCM (16-bit, 24 kHz, mono). Play each piece as it arrives. const ctx = new AudioContext({ sampleRate: 24000 }) const reader = res.body.getReader() let playAt = ctx.currentTime let extra = new Uint8Array(0) // a byte left over from the previous piece while (true) { const { value, done } = await reader.read() if (done) break const bytes = new Uint8Array([...extra, ...value]) const even = bytes.length - (bytes.length % 2) extra = bytes.slice(even) const samples = new Int16Array(bytes.buffer, 0, even / 2) const buffer = ctx.createBuffer(1, samples.length, 24000) buffer.getChannelData(0).set(Float32Array.from(samples, (s) => s / 32768)) const source = ctx.createBufferSource() source.buffer = buffer source.connect(ctx.destination) playAt = Math.max(playAt, ctx.currentTime) source.start(playAt) playAt += buffer.duration }
Do not put your API key in browser code in production. Call /stream from your server and pass the audio through to the browser.

Streaming formats

Used by /stream and /ws (default pcm).

output_audio_codecContent-TypeSample rateNotes
pcm (default)audio/pcm24 kHzRaw headerless 16-bit little-endian, mono. Lowest latency.
wavaudio/wav24 kHz44-byte WAV header first, then PCM. The size fields in the header are 0xFFFFFFFF because the length is unknown while streaming; fix them after the download if a tool needs real sizes (the Python SDK does this for you in generate_audio).
mp3audio/mpeg24 kHzPlays progressively in browsers and most players.
opusaudio/ogg24 kHz input (Ogg Opus, decoded at 48 kHz)Smallest payload.
mulawaudio/basic8 kHzRaw μ-law, mono (telephony).
alawaudio/x-alaw-basic8 kHzRaw A-law, mono (telephony).

linear16 is accepted as an alias of pcm, and ulaw of mulaw. pcm, mulaw and alaw are headerless, so your player needs to be told the format. An unsupported value returns 400 invalid_output_audio_codec.

WebSocket: wss://api.fonada.ai/v1/voice-clone/ws

One connection carries many utterances. Send a JSON message per utterance; the server answers with binary audio frames for it. Utterances are processed one at a time, in the order they are sent.

Connect and authenticate

  • URL: wss://api.fonada.ai/v1/voice-clone/ws
  • Auth: Authorization: Bearer YOUR_API_KEY header, or ?api_key=YOUR_API_KEY in the URL (for clients that cannot set headers).
  • Right after connecting, the server sends a ready message (below).
  • Without a key, the server refuses the handshake with HTTP 403 (most clients report it as a connection failure, close code 1006). With an invalid key, the server sends {"type": "error", "error": "invalid_api_key", ...} and then closes with code 1008.
  • A connection with no message for 5 minutes is closed with code 1000 (idle timeout). Open a new one when you need it.
  • Opening a connection is free and does not count toward the rate limit; each utterance does.
Server → client: ready
{ "type": "ready", "model": "klone_v2_flash", "codecs": ["pcm", "wav", "mp3", "opus", "mulaw", "alaw"], "default_codec": "pcm", "sample_rate": 24000, "channels": 1 }

Client → server: one utterance

JSON
{ "text": "नमस्ते, मैं आपकी कैसे मदद कर सकता हूँ?", "share_id": "YOUR_SHARE_ID", "language": "hi", "output_audio_codec": "pcm", "request_id": "reply-42" }
FieldRequiredDescription
textyesNon-empty, at most 5,000 characters.
share_idone of 2Catalog voice ID.
audio_urlone of 2Reference clip URL (enterprise only). File upload is not available on the WebSocket.
audio_textnoTranscript of the audio_url clip; auto-transcribed when omitted (not available for Urdu).
languageno (hi)ISO code or display name.
output_audio_codecno (pcm)Same values as Streaming formats.
request_idnoYour ID for the utterance; echoed in start, end and error. Generated if omitted.
instruct, num_step, speed, durationnoSame ranges as Voice settings.

Server → client

For each utterance you receive, in order:

  1. start (text frame)
  2. Audio as binary frames (any number). Concatenate or play them in order.
  3. end (text frame)
start
{"type": "start", "request_id": "reply-42", "output_audio_codec": "pcm", "sample_rate": 24000, "channels": 1, "credits_charged": 1.95}
end
{"type": "end", "request_id": "reply-42", "audio_bytes": 74880, "first_audio_ms": 234, "total_ms": 503}

If the utterance fails you get an error instead of end (or instead of start, when it fails before any audio):

error
{"type": "error", "request_id": "reply-42", "status": 400, "error": "invalid_language", "message": "Language 'xx' is not supported by the klone_v2_flash model."}

status is the HTTP status the same error would have on /stream, and error uses the same codes (see Errors). After a failure mid-utterance, audio_bytes_sent tells you how much audio was already sent. The connection stays open after an error, so you can send the next utterance. A message that is not a JSON object returns {"type": "error", "error": "invalid_message"}.

JavaScript (Node.js, ws package)
import fs from 'fs' import WebSocket from 'ws' const ws = new WebSocket('wss://api.fonada.ai/v1/voice-clone/ws', { headers: { Authorization: 'Bearer YOUR_API_KEY' }, }) const file = fs.createWriteStream('hello.pcm') // raw PCM: 16-bit, 24 kHz, mono ws.on('message', (data, isBinary) => { if (isBinary) return file.write(data) // audio const msg = JSON.parse(data) if (msg.type === 'ready') { ws.send(JSON.stringify({ text: 'Hello! How can I help you?', share_id: 'YOUR_SHARE_ID', language: 'en' })) } if (msg.type === 'end') ws.close() // or send the next utterance if (msg.type === 'error') console.error(msg.error, msg.message) })

Play the file with ffplay -f s16le -ar 24000 -ac 1 hello.pcm. In a browser, pass the key as ?api_key= only from trusted environments; for public pages, connect to your own server and relay.

Voice settings

Applied on /stream and /ws. Ignored by /chunks (the Python SDK routes to /stream when you set any of them).

SettingRangeDefaultEffect
instructup to 500 charactersnoneComma-separated style hints, e.g. calm, whispering.
num_step8 – 64server defaultDiffusion steps (advanced).
speed0.25 – 4.0model's natural paceSpeaking rate. >1 faster, <1 slower.
duration0.1 – 60 snoneTarget length for the whole clip. Overrides speed.
speed and duration are currently unreliable on Flash: the output length may not follow them, and sending speed=1.0 can change the pace. Omit them unless you need them. instruct and num_step work as described.

Out-of-range values return 400 invalid_speed, invalid_duration or invalid_num_step. A value that is not a number returns 422 with a validation error body on /stream ({"detail": [{"loc": [...], "msg": ..., "type": ...}]}), and invalid_parameter on the WebSocket.

Pricing

Priced per 10K characters, billed per character of input text. The price is the same for /chunks, /stream and /ws.

ModelPriceCreditsBilling
Klone V2 Flash₹6 per 10K characters~500 creditsBilled per character
  • Paid Voice Arena voices (2x or higher) multiply this rate. Free voices, your own uploads and audio URLs are billed at 1x. Example: 39 characters on a 7x voice costs 13.65 credits.
  • Credits are rounded to 2 decimals, with a minimum of 0.01 credits per request.
  • On /stream and /ws the full text is charged when the request starts. /stream returns the amount in X-Credits-Charged; the WebSocket sends it as credits_charged in start.
  • Failed requests are refunded automatically when they fail before any audio is sent. Once streamed audio has started (including when you disconnect or stop reading a WebSocket utterance early), the request is billed in full.

See the full pricing page.

Rate limits

  • 10 requests per minute per user by default, shared by /chunks, /stream and /ws. Each WebSocket utterance counts as one request; opening a connection does not.
  • On the WebSocket, a rate-limited utterance returns an error message with status: 429 and the connection stays open.
  • A global concurrency cap returns the same error with rate_period: "concurrent" and retry_after_seconds: 5.
  • Flash handles roughly 1.5 to 2 requests per second in total. Under heavy load, latency grows instead of erroring.
  • The wait time is only in the body. There is no Retry-After header.
429 response body
{ "detail": { "error": "rate_limit_exceeded", "message": "Rate limit exceeded. You can make 10 requests per minute.", "details": { "rate_limit": 10, "current_usage": 10, "remaining": 0, "retry_after_seconds": 37, "rate_period": "minute" } } }

Errors

All errors are JSON: {"detail": {"error": "<code>", "message": "<text>"}}

HTTPerrorCause
400empty_texttext is empty or whitespace
400text_too_longtext is longer than 5,000 characters (limit_chars and received_chars in the body); nothing is charged
400invalid_languagelanguage is not in the supported list (response includes supported_languages)
400missing_audio_sourceNo reference source supplied
400ambiguous_audio_sourceMore than one of share_id / audio / audio_url supplied
400empty_audio / audio_decode_failed / audio_too_longReference upload is empty, not valid audio, or longer than 30 s
413audio_too_largeReference upload larger than 10 MB
403enterprise_onlyaudio / audio_url on a non-enterprise tier
403invalid_api_key / inactive_api_keyBad or disabled API key
404voice_not_foundshare_id is not in the catalog
429rate_limit_exceededMore than 10 requests per minute per user, or too many requests in flight (see Rate limits)
429credits_exhaustedInsufficient credits / credit limit reached
500encoding_failedServer-side transcode to the requested codec failed
502upstream_error / upstream_unreachable / upstream_emptyUpstream cloner failed or returned no audio. Credits are refunded automatically

Streaming and WebSocket errors

In addition to the errors above. WebSocket errors carry the same error code and message in a {"type": "error", "status": <http status>, ...} message.

HTTPerrorCause
400invalid_speed / invalid_duration / invalid_num_stepVoice setting out of range (/stream, /ws)
400invalid_parameterVoice setting is not a number (WebSocket only)
422—A form field has the wrong type, e.g. speed=fast on /stream (FastAPI validation body: {"detail": [...]})
400streaming_not_supportedmodel=klone_v2_pro sent to /stream or /ws. Use /v1/voice-clone/chunks (or the /v2/voice-clone/chunks alias) for Pro
400invalid_modelUnknown model on /stream or /ws (response includes supported_models)
— (WebSocket)invalid_messageWebSocket message is not a JSON object
502upstream_stream_brokenThe cloner connection broke after audio had started; the stream ends early and the request is billed

Python SDK

Set model="klone_v2_flash" on TTSClient.generate_audio(). As with Pro, mp3 and opus are limited to 450 characters in the SDK. Streaming and the WebSocket require fonadalabs 3.0.0 or later (pip install -U fonadalabs). See the Klone V2 Pro SDK section for authentication.

Python
from fonadalabs.tts.client import TTSClient client = TTSClient(api_key="YOUR_API_KEY") audio_bytes = client.generate_audio( text="Hello from flash cloning.", language="Hindi", model="klone_v2_flash", share_id="YOUR_SHARE_ID", output_file="flash.wav", )

Stream: stream_audio / stream_audio_async

Python
from fonadalabs.tts.client import TTSClient client = TTSClient(api_key="YOUR_API_KEY") with open("hello.mp3", "wb") as f: for chunk in client.stream_audio("नमस्ते, आपका स्वागत है।", language="hi", share_id="YOUR_SHARE_ID", output_audio_codec="mp3"): f.write(chunk) # or send it to your player print(client.last_stream_info.credits_charged)
Python (async)
import asyncio from fonadalabs.tts.client import TTSClient async def main(): client = TTSClient(api_key="YOUR_API_KEY") with open("hello.pcm", "wb") as f: async for chunk in client.stream_audio_async("Hello!", language="en", share_id="YOUR_SHARE_ID"): f.write(chunk) # raw PCM: 16-bit, 24 kHz, mono asyncio.run(main())

Both accept share_id / audio_url / audio, audio_text, output_audio_codec, instruct, num_step, speed and duration. Invalid arguments raise before anything is sent. Only Flash streams; model="klone_v2_pro" raises TTSError.

WebSocket: stream_session

Python
import asyncio from fonadalabs.tts.client import TTSClient async def main(): client = TTSClient(api_key="YOUR_API_KEY") async with client.stream_session() as session: # one connection, many replies with open("replies.pcm", "wb") as f: for text in ["Hello!", "How can I help you today?"]: async for chunk in session.speak(text, language="en", share_id="YOUR_SHARE_ID"): f.write(chunk) # raw PCM: 16-bit, 24 kHz, mono asyncio.run(main())
  • session.speak(text, language, share_id=... | audio_url=..., audio_text=..., output_audio_codec=..., instruct=..., num_step=..., request_id=...) yields the audio of one utterance.
  • session.ready holds the server's ready message, session.last_info the start details (codec, sample rate, credits) and session.last_end the end message.
  • A rejected utterance raises VoiceCloneError (from fonadalabs.voice_clone; EnterpriseOnlyError, RateLimitError and CreditsExhaustedError are subclasses). The session stays open for the next utterance.
  • If you stop reading an utterance early, the next speak skips its remaining audio automatically (the abandoned utterance is still billed).

Voice settings with generate_audio

generate_audio (one-shot) also accepts instruct, num_step, speed and duration for Flash. When any is set, the SDK collects the audio from /stream and returns it in one piece (with correct WAV sizes).

In SDK 3.0.0, generate_audio also sends speed=1.0 whenever you set one of the other settings, which can change the pace (see Voice settings). Until a fixed release, use stream_audio or stream_session for instruct and num_step; they send only what you pass.

FAQ

Both use POST /v1/voice-clone/chunks with the same reference sources, response headers and errors. Flash is faster, supports 12 languages and takes the whole text in one request (the server splits and merges it). On /chunks it ignores speed, duration, instruct and num_step; they apply on /stream and the WebSocket, which are Flash only. Flash also costs a tenth of Pro; see Pricing.