Streaming transcription
Rem streams interim and final transcripts in real time. Create a session with the samelw_ API key you use for your workspace. The response contains a short-lived WebSocket URL. The key is never placed in the WebSocket URL.
1. Create a session
POST https://app.lucidweights.ai/api/v1/stt/sessions
x-api-key: lw_YOUR_SECRET
Content-Type: application/json
{"language":"ar"}Supported language hints: ar, en, es, fr, hi, ja. Omit language for English. The response has sessionId,sampleRate: 16000, andwsUrl. Use the URL once within two minutes.
2. Send audio
Connect to the returned wsUrl. Send JSON text frames, each containing base64-encoded mono PCM16 little-endian audio at 16 kHz. Each frame may contain up to 32,000 samples (two seconds). Send frames at roughly real-time pace and close the socket when finished.
{"audio_data":"<base64 PCM16 bytes>"}3. Receive transcripts
| Field | Type | Default | Description |
|---|---|---|---|
| transcription | string | - | Recognized text so far, or a completed utterance |
| final | boolean | - | true when the utterance is complete |
| error | string | - | Present when the stream cannot continue |
{"transcription":"مرحبا، كيف الحال؟","final":false}
{"transcription":"مرحبا، كيف الحال؟","final":true}Credits and limits
Rem and Reverie share workspace credits. Each started second of audio accepted by the stream costs one credit; a new session with no audio costs none. The relay stops accepting audio when credits run out. Sessions last at most five minutes. Concurrent Rem stream limits are Free 3, Starter 15, Growth 40, and Scale 100.
Python example
import asyncio, base64, json, os, wave
import requests, websockets
# Use a 16 kHz, mono, signed 16-bit PCM WAV for this example.
response = requests.post(
"https://app.lucidweights.ai/api/v1/stt/sessions",
headers={"x-api-key": os.environ["LUCIDWEIGHTS_API_KEY"]},
json={"language": "ar"},
timeout=15,
)
response.raise_for_status()
ws_url = response.json()["wsUrl"] # Signed, short-lived, single use.
async def main():
async with websockets.connect(ws_url) as ws:
async def receive():
async for message in ws:
event = json.loads(message)
if "error" in event:
raise RuntimeError(event["error"])
if event.get("transcription"):
print("FINAL" if event.get("final") else "INTERIM", event["transcription"])
reader = asyncio.create_task(receive())
with wave.open("sample-16k-mono.wav", "rb") as audio:
assert audio.getnchannels() == 1 and audio.getframerate() == 16000
assert audio.getsampwidth() == 2
while chunk := audio.readframes(1600):
await ws.send(json.dumps({"audio_data": base64.b64encode(chunk).decode()}))
await asyncio.sleep(len(chunk) / 2 / 16000)
await asyncio.sleep(2) # Allow final transcript to arrive.
await ws.close()
await reader
asyncio.run(main())