[{"data":1,"prerenderedAt":4},["ShallowReactive",2],{"article-doc:cookbook\u002Fchoose-realtime-vs-batch:en":3},"---\ntitle: \"How to choose between realtime and batch transcription\"\ndescription: \"Realtime WebSocket, Segment HTTP, or Batch jobs? The decision isn't 'do I need it live' — it's who does VAD and what happens to silence. A decision table and a minimal example for each plane.\"\nseries: \"A\"\ndifficulty: \"beginner\"\nreading_time: \"7 min\"\nstatus: \"draft\"\nlang: \"en\"\nrelated_docs:\n  - \"\u002Fstart\u002Fchoose-an-api\"\n  - \"\u002Fapi-reference\u002Fsegment-transcription\"\n  - \"\u002Fapi-reference\u002Fbatch-jobs\"\n  - \"\u002Frealtime\u002Fquickstart\"\nrelated_cookbook:\n  - \"stream-mic-audio-over-websocket\"\n  - \"stable-captions-no-jitter\"\nsource_urls:\n  - \"https:\u002F\u002Fdocs.lansonai.com\u002Fstart\u002Fchoose-an-api\"\n  - \"https:\u002F\u002Fdocs.lansonai.com\u002Fapi-reference\u002Fsegment-transcription\"\n  - \"https:\u002F\u002Fdocs.lansonai.com\u002Fapi-reference\u002Fbatch-jobs\"\nclaim_sources:\n  - claim: \"Three planes: Realtime WS (server VAD), Segment HTTP (client VAD, sync 200), Batch jobs (server time-slice, 202 + poll).\"\n    source: \"lanson-audio-docs\u002Fcontent\u002F1.start\u002F5.choose-an-api.md (decision table + comparison)\"\n    type: \"fact\"\n  - claim: \"Segment has no server-side silence filtering; whatever you POST is transcribed and billed.\"\n    source: \"lanson-audio-docs\u002Fcontent\u002F6.api-reference\u002F5.segment-transcription.md (What it does)\"\n    type: \"fact\"\n  - claim: \"Segment wall-clock budget is roughly ≤1.6s on the warm path; no server HTTP auto-retry.\"\n    source: \"lanson-audio-docs\u002Fcontent\u002F6.api-reference\u002F5.segment-transcription.md (Latency and retries)\"\n    type: \"fact\"\n  - claim: \"Batch workflow_id is cf_ + 64 hex, not a UUID.\"\n    source: \"lanson-audio-docs\u002Fcontent\u002F6.api-reference\u002F6.batch-jobs.md (Response 202)\"\n    type: \"fact\"\ncreated_at: \"2026-08-27\"\nupdated_at: \"2026-08-27\"\n---\n\n# How to choose between realtime and batch transcription\n\n> **TL;DR** — \"Realtime vs batch\" is the wrong framing. The real decision is **who does VAD** (server or you) and **what happens to silence**. LansonAI exposes three planes — Realtime WS, Segment HTTP, Batch jobs — and the right one is determined by your audio's shape (stream \u002F clip \u002F file) and your latency need. Pick by the table, not by instinct.\n\n## Problem\n\nYou have audio and you need text. It sounds like one problem, but it's three different problems:\n\n- A person is **talking right now** and you want text as they speak.\n- Your app **already cut the speech** into short clips (a voice UI) and you want each clip transcribed.\n- You have a **finished file** (a meeting recording, a podcast) at a URL and you want it processed.\n\nMost integrations pick a plane by instinct — \"I'll just stream everything\" or \"I'll just upload a file\" — and then hit a wall: unexpected latency, billing for silence, or a WebSocket where they needed a simple HTTP call. The wall is almost always the same root cause: **nobody decided who does VAD.**\n\n## Architecture\n\nLansonAI exposes three transcription planes. The differentiator that matters is **VAD ownership** and **when you need the text**:\n\n| Dimension | Realtime WS | Segment HTTP | Batch jobs |\n|---|---|---|---|\n| **When you need text** | While they're speaking | Per clip, immediately | Minutes later (async) |\n| **Who does VAD** | **Server** (on the stream) | **You** (client) | **Server** (time-slice) |\n| Transport | WebSocket | HTTP POST | HTTP POST + GET poll |\n| Input | PCM16LE stream | Multipart `file` | JSON `audio_url` |\n| Output | Event stream | Transcript JSON | Full job JSON |\n| Latency | Milliseconds | ~≤1.6 s (sync) | Minutes |\n| Success signal | WS events | **200** | **202** + poll |\n\nThe three planes, in one line each:\n\n- **Realtime WS** (`WS \u002Fv1\u002Faudio\u002Ftranscriptions\u002Fstream`) — you send a continuous PCM stream; the server runs VAD and emits utterances as they stabilize. For live captions and dialogue.\n- **Segment HTTP** (`POST \u002Fv1\u002Faudio\u002Ftranscriptions`) — you send one pre-cut clip (multipart `file`); you get a synchronous `200` with the transcript. For voice UIs that already detect boundaries locally.\n- **Batch jobs** (`POST \u002Fv1\u002Faudio\u002Ftranscriptions\u002Fjobs`) — you send a public `audio_url`; you get `202` and poll. The server slices by time, transcribes chunks concurrently, and aggregates. For meeting\u002Fpodcast files.\n\n> **The trap to know up front** — Segment has **no server-side silence filtering**. Whatever you POST is transcribed and billed. If your client VAD is loose and you send 3 seconds of silence, you pay for 3 seconds of silence. Realtime and Batch handle silence for you; Segment does not, because *you* own the boundary.\n\n## Code\n\nMinimal, real example for each plane. (Full audio pipeline for the WS plane is in [How to stream microphone audio over WebSocket](\u002Fcookbook\u002Fstream-mic-audio-over-websocket).)\n\n### Realtime WS — text while they speak\n\n```javascript\n\u002F\u002F Server-side: Bearer header. Browser: ?access_token=rt_... (from your backend)\nconst ws = new WebSocket(\"wss:\u002F\u002Faudio.lansonai.com\u002Fv1\u002Faudio\u002Ftranscriptions\u002Fstream\");\n\u002F\u002F ws = new WebSocket(url, { headers: { Authorization: `Bearer ${SK}` } })  \u002F\u002F Node\n\nws.onopen = () => {\n  ws.send(JSON.stringify({ type: \"session.update\", language: \"zh\" }));\n  \u002F\u002F ... send PCM16LE\u002F16kHz\u002Fmono frames as input_audio_buffer.append ...\n  \u002F\u002F ws.send(JSON.stringify({ type: \"input_audio_buffer.append\", audio: \"\u003Cbase64>\" }));\n  \u002F\u002F ws.send(JSON.stringify({ type: \"input_audio_buffer.flush\" })); \u002F\u002F end of segment\n};\n\nws.onmessage = (e) => {\n  const ev = JSON.parse(e.data);\n  if (ev.type === \"conversation.item.input_audio_transcription.completed\") {\n    console.log(`[${ev.utterance_index}] ${ev.text}`); \u002F\u002F stable text\n  }\n};\n```\n\n### Segment HTTP — you already cut the clip\n\n```bash\ncurl -X POST https:\u002F\u002Faudio.lansonai.com\u002Fv1\u002Faudio\u002Ftranscriptions \\\n  -H \"Authorization: Bearer sk-...\" \\\n  -F \"file=@utterance.wav\" \\\n  -F \"language=zh\" \\\n  -F \"response_format=verbose_json\"\n# → 200 { \"text\": \"...\", \"language\": \"zh\", \"duration\": 1.2, ... }\n```\n\n### Batch jobs — you have a file at a URL\n\n```bash\n# 1. Submit (202)\ncurl -X POST https:\u002F\u002Faudio.lansonai.com\u002Fv1\u002Faudio\u002Ftranscriptions\u002Fjobs \\\n  -H \"Authorization: Bearer sk-...\" -H \"Content-Type: application\u002Fjson\" \\\n  -d '{ \"audio_url\": \"https:\u002F\u002Fcdn.example.com\u002Fmeeting.wav\", \"language\": \"zh\" }'\n# → { \"workflow_id\": \"cf_5519...\", \"status\": \"queued\", \"poll_endpoint\": \"GET \u002Fv1\u002Faudio\u002Ftranscriptions\u002Fjobs\u002Fcf_5519...\" }\n\n# 2. Poll until status == \"complete\"\ncurl https:\u002F\u002Faudio.lansonai.com\u002Fv1\u002Faudio\u002Ftranscriptions\u002Fjobs\u002Fcf_5519... \\\n  -H \"Authorization: Bearer sk-...\"\n```\n\n## Result\n\nYou pick a plane you can defend in one sentence:\n\n- \"Live captions → **Realtime**, because the server should run VAD on a stream and I need text in milliseconds.\"\n- \"Voice input → **Segment**, because my client already detects utterance boundaries and I want a synchronous result per clip.\"\n- \"Podcast processing → **Batch**, because I have a file at a URL and can wait minutes for a complete, aggregated transcript.\"\n\nAnd the whole decision compresses into three questions you can run top-down:\n\n```text\nIs someone talking right now, and must text appear while they speak?\n├─ Yes → Realtime WS  (server VAD on a continuous stream)\n└─ No  → Have you already cut the speech into short clips (your own VAD)?\n          ├─ Yes → Segment HTTP  (sync 200 per clip; you own silence)\n          └─ No  → Is it a complete recording at a URL?\n                   ├─ Yes → Batch jobs  (202 + poll; server time-slices)\n                   └─ No  → Capture or upload it first, then re-ask.\n```\n\nThe latency figures in the table come from the API contract itself (Realtime: millisecond-level event stream; Segment: ~≤1.6s wall-clock budget on the warm path; Batch: minutes, async). Cost varies by plan and usage — check the rate-limits page before committing.\n\n## Why it matters\n\n1. **VAD ownership is the real decision, not \"realtime vs batch.\"** The question \"do I need it live?\" gets you 80% there, but the deeper question is \"who cuts the speech into utterances, and who is responsible for silence?\" Answer that and the plane chooses itself.\n\n2. **Silence is a billing and quality decision.** On Segment you own the boundary, so you own the silence — send it and you're billed for it. On Realtime and Batch the service handles it. Knowing which plane pushes silence-handling onto *you* prevents a class of \"why am I paying for nothing\" surprises.\n\n3. **The transferable lesson.** Match the transport to the shape of your data: a **stream** wants a full-duplex socket, a **clip** wants a synchronous request, a **file** wants an async job you poll. Reaching for the wrong shape is how you end up writing reconnect logic you didn't need, or blocking a request for minutes.\n\n---\n\n### Further reading (Docs)\n\n- [Choose an API](https:\u002F\u002Fdocs.lansonai.com\u002Fstart\u002Fchoose-an-api) — Decision table + overview of the three planes\n- [Segment Transcription API](https:\u002F\u002Fdocs.lansonai.com\u002Fapi-reference\u002Fsegment-transcription) — Client-VAD synchronous interface\n- [Batch Transcription Jobs](https:\u002F\u002Fdocs.lansonai.com\u002Fapi-reference\u002Fbatch-jobs) — Asynchronous job interface\n- [Realtime Quickstart](https:\u002F\u002Fdocs.lansonai.com\u002Frealtime\u002Fquickstart) — Minimal runnable example\n\n### Related cookbook recipes\n\n- [How to stream microphone audio over WebSocket](\u002Fcookbook\u002Fstream-mic-audio-over-websocket) — Audio pipeline for the Realtime plane\n- [How to build realtime captions that don't jitter](\u002Fcookbook\u002Fstable-captions-no-jitter) — Rendering side for the Realtime plane\n",1792419253418]