Quick Start (WebSockets)
Palabra API client
Consider using the Palabra API Python client for WS-based integrations.
How it works
With the WebSocket transport, everything happens over a single connection:
- You connect to the WebSocket endpoint with your API Key — a streaming session is created for you automatically.
- You send a
set_taskmessage with your translation settings. - You stream your audio to Palabra as base64-encoded chunks.
- Palabra transcribes, translates, and synthesizes your speech in real time, then streams the translated audio and text back through the same connection.
Step 1. Get an API Key
Create an API Key on the Palabra API Keys page. See Authentication for details.
Step 2. Connect to the WebSocket API
Connect to the endpoint below, passing your API Key as the token query parameter (or in the Authorization header). The server validates the key and creates a streaming session for the lifetime of the connection automatically.
wss://streaming.palabra.ai/streaming-api/{random_hash}/v1/speech-to-speech/stream?token=<API_KEY>
Replace the {random_hash} path segment with any random URL-safe string generated by your client for the connection — it is used to distribute connections across translation servers.
import uuid
import websockets
async def connect_websocket(api_key: str):
ws_url = (
"wss://streaming.palabra.ai/streaming-api/"
f"{uuid.uuid4().hex}/v1/speech-to-speech/stream"
)
websocket = await websockets.connect(
f"{ws_url}?token={api_key}", ping_interval=10, ping_timeout=30
)
print("🔌 Connected to WebSocket")
return websocket
Alternative: connect with a session token
If you prefer to manage streaming sessions yourself, create a session via POST /session-storage/session. The response contains the ws_url and the publisher token: connect to ws_url, passing the publisher token as the token query parameter.
import websockets
async def connect_websocket_with_session(ws_url: str, publisher_token: str):
full_url = f"{ws_url}?token={publisher_token}"
websocket = await websockets.connect(full_url, ping_interval=10, ping_timeout=30)
print("🔌 Connected to WebSocket")
return websocket
Step 3. Configure the Translation
Send a set_task message with your translation settings. The format, sample_rate, and channels you declare here must match the audio you will send in Step 4.
See the Translation management API for the full command reference and the Translation settings breakdown for every available option.
import json
async def configure_translation(websocket, source_lang: str, target_langs: list):
settings = {
"message_type": "set_task",
"data": {
"input_stream": {
"content_type": "audio",
"source": {
"type": "ws",
"format": "pcm_s16le",
"sample_rate": 24000,
"channels": 1
}
},
"output_stream": {
"content_type": "audio",
"target": {
"type": "ws",
"format": "pcm_s16le",
"sample_rate": 24000,
"channels": 1
}
},
"pipeline": {
"preprocessing": {},
"transcription": {
"source_language": source_lang
},
"translations": [
{
"target_language": lang,
"speech_generation": {}
} for lang in target_langs
]
}
}
}
await websocket.send(json.dumps(settings))
print(f"⚙️ Translation configured: {source_lang} → {target_langs}")
Give the server a moment (2–3 seconds) to apply the settings before you start streaming audio.
Step 4. Send Audio Data
Capture audio from your source (for example, a microphone) and send it as base64-encoded chunks in input_audio_data messages. The recommended chunk length is 320 ms.
import asyncio
import base64
import json
import queue
import threading
import time
import numpy as np
import sounddevice as sd
async def stream_microphone(websocket):
sample_rate = 24000 # must match `input_stream` in set_task
chunk_duration = 0.32 # 320 ms chunks recommended
chunk_samples = int(sample_rate * chunk_duration)
audio_queue = queue.Queue(maxsize=100)
stop_event = threading.Event()
def input_callback(indata, frames, time_info, status):
try:
audio_queue.put_nowait(np.frombuffer(indata, dtype=np.int16).copy())
except queue.Full:
pass
def recording_thread():
with sd.RawInputStream(
samplerate=sample_rate,
channels=1,
dtype='int16',
callback=input_callback,
blocksize=int(sample_rate * 0.02) # 20 ms callback
):
print("🎤 Microphone started")
while not stop_event.is_set():
time.sleep(0.01)
threading.Thread(target=recording_thread, daemon=True).start()
buffer = np.array([], dtype=np.int16)
while True:
try:
audio_data = audio_queue.get(timeout=0.1)
buffer = np.concatenate([buffer, audio_data])
while len(buffer) >= chunk_samples:
chunk = buffer[:chunk_samples]
buffer = buffer[chunk_samples:]
message = {
"message_type": "input_audio_data",
"data": {
"data": base64.b64encode(chunk.tobytes()).decode("utf-8")
}
}
await websocket.send(json.dumps(message))
# Important: pace audio to the real-time rate
await asyncio.sleep(chunk_duration)
except queue.Empty:
await asyncio.sleep(0.001)
Step 5. Receive and Play Translated Audio
Listen for messages on the same WebSocket. Palabra sends transcriptions, translations, and TTS audio chunks (output_audio_data).
import json
import base64
import queue
import numpy as np
import sounddevice as sd
async def receive_and_play(websocket):
sample_rate = 24000 # must match `output_stream` in set_task
audio_queue = queue.Queue(maxsize=100)
buffer = np.array([], dtype=np.int16)
def audio_callback(outdata, frames, time_info, status):
nonlocal buffer
while len(buffer) < frames:
try:
buffer = np.concatenate([buffer, audio_queue.get_nowait()])
except queue.Empty:
break
if len(buffer) >= frames:
outdata[:] = buffer[:frames].reshape(-1, 1)
buffer = buffer[frames:]
else:
outdata.fill(0)
output_stream = sd.OutputStream(
samplerate=sample_rate,
channels=1,
dtype='int16',
callback=audio_callback,
blocksize=int(sample_rate * 0.02)
)
output_stream.start()
print("🔊 Audio playback started")
async for message in websocket:
data = json.loads(message)
# Some messages arrive with `data` as a JSON string — parse it
if isinstance(data.get("data"), str):
data["data"] = json.loads(data["data"])
msg_type = data.get("message_type")
if msg_type == "current_task":
print("📝 Task confirmed")
elif msg_type == "output_audio_data":
audio_bytes = base64.b64decode(data["data"]["data"])
audio_array = np.frombuffer(audio_bytes, dtype=np.int16)
try:
audio_queue.put_nowait(audio_array)
except queue.Full:
pass
elif msg_type == "partial_transcription":
text = data["data"]["transcription"]["text"]
lang = data["data"]["transcription"]["language"]
print(f"\r\033[K💬 [{lang}] {text}", end="", flush=True)
elif msg_type == "validated_transcription":
text = data["data"]["transcription"]["text"]
lang = data["data"]["transcription"]["language"]
print(f"\r\033[K✅ [{lang}] {text}")
Complete Example
A minimal runner combining all the steps.
import asyncio
import os
import signal
async def main():
signal.signal(signal.SIGINT, lambda s, f: os._exit(0))
print("🚀 Palabra WebSocket Client")
# Step 1: Your API Key
api_key = os.getenv("PALABRA_API_KEY")
# Step 2: Connect to WebSocket (a session is created automatically)
websocket = await connect_websocket(api_key)
# Step 3: Configure translation and wait for it to apply
await configure_translation(websocket, "en", ["es"])
await asyncio.sleep(3) # see the tip below for a deterministic check
# Steps 4 & 5: Stream the microphone and play translated audio
receive_task = asyncio.create_task(receive_and_play(websocket))
stream_task = asyncio.create_task(stream_microphone(websocket))
print("\n🎧 Listening... Press Ctrl+C to stop\n")
try:
await asyncio.gather(receive_task, stream_task)
except KeyboardInterrupt:
print("\n🛑 Shutdown complete")
if __name__ == "__main__":
asyncio.run(main())
Summary
Once the WebSocket connection is open and your set_task settings are applied, Palabra processes your audio stream in real time: it transcribes your speech, translates it into the target languages, and streams both the text and the synthesized audio back through the same connection.
Good to know
- The server does not acknowledge
set_taskdirectly. Instead of a fixedsleep, you can pollget_task(no more than once per 2 seconds): it returns aNOT_FOUNDerror until the pipeline has started, and acurrent_taskmessage once your task is live. - Sessions created automatically on connect (API Key auth) are cleaned up for you after the connection closes. If you create sessions yourself, note that unused sessions stay active for at least 1 minute — to avoid hitting the limit on simultaneously active sessions, delete sessions you no longer need. See Session Management.
- To pause, flush, or stop the translation, see the Translation management API.
Need help?
If you have any questions or need assistance, contact us at api.support@palabra.ai.