audio Binary frame
Binary frame of mono PCM16 little-endian audio at the sample_rate you connected with. At most 64 KiB.
Developers / Speech to Text
wss://api.echonova.ai/v1/speech-to-text/streamOpen one socket per live audio source. Send mono PCM16 little-endian audio as binary frames. Send end_segment when the speaker pauses to get that segment's transcript right away; without it the server cuts a segment every 15 seconds. Each transcript is final: there are no interim or partial results. Each segment is charged at the Live Speech to Text rate when its transcript is sent.
Try it is not available here: a browser cannot send an API key on a WebSocket, so connect from your server. Read the streaming guide · Try live transcription in the portal
GET Upgrade: websocket
Bearer, a space, then your API key, which starts with ek_. Create keys in the portal under Developers, API Keys.
Sample rate of the audio you send, in Hz.
Spoken language. Required. ar for Arabic and mixed Arabic/English; en only for wholly English speech.
Audio encoding. Only 16-bit signed little-endian mono PCM is accepted.
audio Binary frameBinary frame of mono PCM16 little-endian audio at the sample_rate you connected with. At most 64 KiB.
end_segmentTranscribe the audio sent since the last segment now. Send it when the speaker pauses.
{
"type": "end_segment"
}stopTranscribe any remaining audio, send the last transcripts and session.ended, then close.
{
"type": "stop"
}pingOptional keep-alive. The server answers with pong.
{
"type": "ping"
}session.startedSent once after the socket opens. Start sending audio after it arrives.
Identifies this session in support requests.
The language you connected with.
The sample rate the server expects, in Hz.
Seconds until the server ends the session.
{
"type": "session.started",
"sessionId": "c8a1f2e4-5b6d-4e7f-9a0b-1c2d3e4f5a6b",
"language": "ar",
"sampleRate": 16000,
"maxSessionSeconds": 1500
}transcriptThe final transcript of one segment. Segments can arrive slightly after you send end_segment; match them by segmentId.
Identifies the segment.
The transcript. Empty when the segment held no speech.
The detected language, ar or en.
Length of the segment's audio, in seconds.
Credits charged for this segment.
Always true: there are no interim results.
{
"type": "transcript",
"segmentId": "6720f3a1c4b5d6e7f8a9b0c1",
"text": "مرحبا، كيف يمكنني مساعدتك اليوم؟",
"language": "ar",
"durationSeconds": 2.4,
"creditsConsumed": 8,
"isFinal": true
}session.endedThe last message. The server closes the socket right after it.
Why the session ended.
Seconds of audio transcribed in this session.
Credits charged in this session.
{
"type": "session.ended",
"reason": "stopped",
"totalSeconds": 42.6,
"creditsConsumed": 142
}errorSomething went wrong. Fatal errors are followed by a close frame; others leave the socket open.
Text to Speech only: the synthesis that failed.
Machine-readable reason.
Human-readable explanation.
{
"type": "error",
"code": "validation_failed",
"message": "text must be shorter than or equal to 500 characters"
}pongThe answer to ping.
{
"type": "pong"
}Plain HTTP answers to the upgrade request, in the usual envelope. Nothing is charged. All errors
When. sample_rate is missing, not a whole number, or out of range.
Fix. Connect with ?sample_rate= set to your audio's rate.
When. language is missing or not ar or en.
Fix. Connect with ?language= set to ar or en.
When. encoding is not an allowed value.
Fix. Leave encoding out, or send pcm_s16le.
When. The Authorization header is missing or is not Bearer ek_..., no key matches the token, or the key is deleted, disabled or its account is closed.
Fix. Send Authorization: Bearer ek_... with an enabled key from this environment.
When. The workspace balance is empty.
Fix. Add credits to the workspace.
When. The key is restricted and Speech to Text is not among its services.
Fix. Edit the key and grant Speech to Text, or use another key.
When. The subscription is in a state other than active or trial.
Fix. Renew the subscription.
When. The workspace that owns the key has no subscription.
Fix. Check the workspace's plan in the portal.
When. The key opened more than 100 connections in 60 seconds.
Fix. Wait Retry-After seconds, then reconnect.
When. The workspace already has 10 streams open.
Fix. Close a stream, or reuse one socket for many syntheses.
When. The key has reached its Credit Limit for this billing cycle.
Fix. Raise or clear the limit on the key, or wait for the next cycle.
When. Streaming is turned off on this environment.
Fix. Contact support.
import asyncio
import json
import wave
import websockets # pip install "websockets>=14"
URL = "wss://api.echonova.ai/v1/speech-to-text/stream"
API_KEY = "YOUR_API_KEY"
async def main():
# A 16-bit mono PCM WAV file; its header gives the sample rate.
with wave.open("audio.wav", "rb") as wav:
if wav.getsampwidth() != 2 or wav.getnchannels() != 1:
raise SystemExit("audio.wav must be 16-bit mono PCM")
rate = wav.getframerate()
pcm = wav.readframes(wav.getnframes())
async with websockets.connect(
f"{URL}?sample_rate={rate}&language=ar",
additional_headers={"Authorization": f"Bearer {API_KEY}"},
) as ws:
print(json.loads(await ws.recv())) # session.started
async def send_audio():
step = rate // 10 * 2 # 100 ms of 16-bit samples, in bytes
for i in range(0, len(pcm), step):
await ws.send(pcm[i : i + step]) # bytes: a binary frame
await asyncio.sleep(0.1) # real time, like a live source
await ws.send(json.dumps({"type": "end_segment"}))
await ws.send(json.dumps({"type": "stop"}))
sender = asyncio.create_task(send_audio())
async for raw in ws: # ends when the server closes after session.ended
message = json.loads(raw)
if message["type"] == "transcript":
print(message["text"])
elif message["type"] == "error":
print("error:", message["code"], message["message"])
elif message["type"] == "session.ended":
print("ended:", message["reason"], "credits:", message["creditsConsumed"])
await sender
asyncio.run(main()){"type":"session.started","sessionId":"c8a1f2e4-5b6d-4e7f-9a0b-1c2d3e4f5a6b","language":"ar","sampleRate":16000,"maxSessionSeconds":1500}binary: PCM16 framebinary: PCM16 frame{"type":"end_segment"}{"type":"transcript","segmentId":"6720f3a1c4b5d6e7f8a9b0c1","text":"مرحبا، كيف يمكنني مساعدتك اليوم؟","language":"ar","durationSeconds":2.4,"creditsConsumed":8,"isFinal":true}{"type":"stop"}{"type":"session.ended","reason":"stopped","totalSeconds":42.6,"creditsConsumed":142}close 1000