Developer platform
API examples
Copy-ready examples for the supported REST, Server-Sent Events and WebSocket workflows. Every credential is a placeholder and must be replaced server-side.
Security
Generate a finished audio file
Use the bytes endpoint for a short, non-realtime request. The response body is binary audio, so save it with the format selected in the JSON body.
curl -X POST https://studio.evomlabs.com/api/v1/tts/bytes \
-H "Authorization: Bearer vc_sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"Xin chào","language":"vi","format":"wav"}' \
--output hello.wavStream Text-to-Speech
Use SSE when one POST should progressively return base64 PCM16 events. Use WebSocket for a long-lived, bidirectional voice-agent connection.
Server-Sent Events
curl -N -X POST https://studio.evomlabs.com/api/v1/tts/sse \
-H "Authorization: Bearer vc_sk_live_YOUR_KEY" \
-H "Accept: text/event-stream" \
-H "Content-Type: application/json" \
-d '{"text":"Xin chào","language":"vi"}'event: start
data: {"sample_rate":24000,"channels":1,"format":"pcm_s16le","encoding":"base64"}
event: audio
data: {"sequence":1,"audio":"<base64 PCM16 little-endian>"}
event: done
data: {"chunks":12,"duration_ms":5230,"elapsed_ms":6100}WebSocket
import json, requests, websocket
BASE_URL = "https://studio.evomlabs.com"
API_KEY = "vc_sk_live_YOUR_KEY"
text = "Xin chào, đây là bài test giọng nói."
# 1. Ask for a short-lived token. It expires in 60 seconds, so open the
# socket immediately afterwards.
token = requests.post(
f"{BASE_URL}/api/v1/tts/stream-token",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"text_length": len(text)},
).json()["data"]
# 2. Always connect to the ws_url the response returned.
ws = websocket.create_connection(f"{token['ws_url']}?token={token['stream_token']}")
ws.send(json.dumps({"type": "start", "text": text, "language": "vi"}))
pcm = bytearray()
while True:
frame = ws.recv()
if isinstance(frame, bytes):
pcm.extend(frame) # PCM16 mono at 24 kHz
continue
event = json.loads(frame)
if event["type"] in ("done", "error", "cancelled"):
break
ws.close()
print("received", len(pcm), "bytes of audio")Transcribe an audio file
Upload the entire recording as multipart/form-data and wait for the final transcript object.
curl -X POST "https://studio.evomlabs.com/api/v1/stt/transcriptions" \
-H "Authorization: Bearer stt_sk_live_YOUR_KEY" \
-F "audio=@recording.wav" \
-F "language=auto"import requests
with open("recording.wav", "rb") as audio:
response = requests.post(
"https://studio.evomlabs.com/api/v1/stt/transcriptions",
headers={"Authorization": "Bearer stt_sk_live_YOUR_KEY"},
files={"audio": audio},
data={"language": "auto"},
timeout=1800,
)
response.raise_for_status()
print(response.json()["text"])Transcribe a live microphone
Auto mode uses server VAD. Manual mode waits for commit and suits Push-to-Talk or a client that already has VAD.
Automatic VAD
const ws = new WebSocket(
"wss://studio.evomlabs.com/api/public/v1/stt/ws" +
"?token=stt_sk_live_YOUR_KEY&language=auto"
);
ws.addEventListener("open", () => {
ws.send(JSON.stringify({
type: "start",
mode: "auto",
language: "auto",
sample_rate: 48000,
format: "pcm_s16le",
}));
});
ws.addEventListener("message", (event) => {
const message = JSON.parse(event.data);
if (message.type === "partial" || message.type === "segment") {
renderTranscript(message.combined_text);
}
if (message.type === "final") {
renderTranscript(message.text);
}
if (message.type === "error") {
console.error(message.code, message.message);
}
});
// Send PCM16 frames with ws.send(arrayBuffer).
function stopTranscription() {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: "stop" }));
}
}Manual commit
// Start with: { "type": "start", "mode": "manual", ... }
// Send PCM16 frames, then commit exactly that buffered speech:
ws.send(JSON.stringify({ type: "commit" }));
// The server returns one final segment. Send commit again for the next turn,
// or stop to commit a sufficiently long trailing buffer and close the session.
ws.send(JSON.stringify({ type: "stop" }));Clone a voice and inspect usage
These calls require an account key. Voice enrollment requires audioUpload; key inventory requires keyManagement. A disabled permission returns 403 PERMISSION_DENIED.
Voice Clone
curl -X POST https://studio.evomlabs.com/api/v1/voices \
-H "Authorization: Bearer vc_ak_live_YOUR_KEY" \
-F "file=@sample.wav" \
-F "name=Authorized sample voice" \
-F "gender=female" \
-F "consent=true"API keys and remaining usage
curl https://studio.evomlabs.com/api/v1/keys -H "Authorization: Bearer vc_ak_live_YOUR_KEY"{
"ok": true,
"data": {
"account_usage": {
"tts": {"limit":500000,"used":1234,"remaining":498766,"unlimited":false},
"stt": {"limit":100000,"used":50,"remaining":99950,"unlimited":false},
"reset_date": "2026-09-01T00:00:00.000Z",
"reset_interval": "month",
"reset_interval_count": 1
},
"keys": {"admin":[],"tts":[],"stt":[]}
}
}