पेज नहीं, स्ट्रीम का पता डालें। किसी स्टेशन का असली ऑडियो .mp3, .aac या .m3u8 पर खत्म होने वाले पते पर होता है, और एक बार लाइव ट्रांसक्रिप्शन उसे पकड़ ले, तो किसी को सुनते रहने की ज़रूरत नहीं।
एक न्यूज़रूम जो तीन रेडियो स्टेशन, दो टेलीविज़न स्ट्रीम और कुछ YouTube लाइव पर नज़र रखता है, उसके सामने ट्रांसक्रिप्शन की समस्या से पहले स्टाफ़ की समस्या होती है। किसी को सुनते रहना होता है, और किसी को रिकॉर्ड दबाना होता है। स्ट्रीम के स्रोत वाला लाइव ट्रांसक्रिप्शन ये दोनों काम संभाल लेता है: उसे स्ट्रीम का पता दें, और सर्वर उसे खोलता है, डिकोड करता है और जैसे-जैसे प्रसारण होता है, वैसे-वैसे ट्रांसक्रिप्ट करता है। इसलिए जब प्रेस कॉन्फ़्रेंस जल्दी शुरू हो जाए, तब भी किसी को डेस्क पर होना नहीं पड़ता। असली मुश्किल, जैसा कि पता चलता है, पता ढूँढना है।
लाइव ट्रांसक्रिप्शन अपना ऑडियो ऐसी स्ट्रीम से ले सकता है जिसे सर्वर खुद खींचता है: इंटरनेट रेडियो स्टेशन, HLS प्लेलिस्ट (.m3u8), DASH मैनिफ़ेस्ट (.mpd), या YouTube लाइव। डैशबोर्ड में यह Transcriptions → New → Live पर बनने वाला नया लाइव ट्रांसक्रिप्शन है, जिसका स्रोत माइक्रोफ़ोन की जगह एक स्ट्रीम होता है। पता ट्रांसक्रिप्शन बनाते समय तय होता है और कभी नहीं बदलता, इसलिए एक मॉनिटर एक बार सेट कर लिया जाता है और जब भी स्टेशन रिकॉर्ड करने लायक हो, तब शुरू कर दिया जाता है।
पता वेब पेज का नहीं, स्ट्रीम का ही होना चाहिए, जो उस पेज को चलाती है। किसी स्टेशन का पेज HTML होता है; उसका ऑडियो कहीं और से आता है, आमतौर पर किसी Icecast या Shoutcast सर्वर से, और पेज का प्लेयर पेज लोड होने के बाद JavaScript से वह पता ढूँढता है। साइट पर "Listen", "Direct stream" या "Other players" लिंक देखें: वह .mp3, .aac, .m3u, .m3u8 या .pls पर खत्म होता है। .pls या .m3u भी ऑडियो नहीं है। यह कुछ पंक्तियों की एक छोटी टेक्स्ट फ़ाइल है, जिसमें हर पंक्ति किसी स्ट्रीम का पता देती है; यह इसलिए होती है ताकि डेस्कटॉप प्लेयर को एक ही लिंक दिया जा सके, और VocalStack वह स्ट्रीम ट्रांसक्रिप्ट करता है जिसका वह नाम देती है। अगर आप इसकी जगह पेज डालते हैं, तो VocalStack उसे माँगता है, HTML वापस आते देखता है और आपको यह बता देता है। हर स्टेशन अपनी स्ट्रीम थोड़े अलग तरीके से छुपाता है; ज़्यादातर उसे "Listen" लिखे बटन के पीछे छुपाते हैं, और पेज पर वही एक चीज़ है जिस पर क्लिक करना ज़रूरी नहीं।
दो तरह के लिंक सीधे मना कर दिए जाते हैं। ऐसे पेज जो मीडिया चलाते हैं, जैसे Spotify, SoundCloud या Twitch, किसी कनेक्शन से पहले ही लौटा दिए जाते हैं। कॉपी-प्रोटेक्टेड स्ट्रीम भी समर्थित नहीं हैं: जो प्लेलिस्ट या मैनिफ़ेस्ट DRM लाइसेंस माँगता है, वह एक ऐसे संदेश के साथ विफल होता है जो यही बताता है। कुछ स्ट्रीम स्टेशन के देश के बाहर से आने वाले अनुरोधों को भी ठुकरा देती हैं, इसलिए जो पता दफ़्तर में चलता है, वह विदेश के किसी सर्वर से शायद न चले। अगर कोई स्ट्रीम ब्राउज़र में चलती है पर VocalStack में नहीं, तो स्टेशन दूसरे प्लेयर को नहीं आने दे रहा; उससे ऐसा पता माँगें जिसे दूसरे ऐप इस्तेमाल कर सकें। ज़्यादातर MP3, AAC या HLS स्ट्रीम देते हैं। सही पता मिल जाए, तो अगला सवाल यह है कि वह कभी खत्म होता है या नहीं।
लाइव स्ट्रीम कभी खत्म नहीं होती, इसलिए वह कोई फ़ाइल नहीं है। यह फ़र्क़ खासकर YouTube के लिए मायने रखता है। जो YouTube लाइव प्रसारण में है, वह लाइव ट्रांसक्रिप्शन में जाता है, और डैशबोर्ड उसे प्रसारण के साथ-साथ फ़ॉलो करता है। जो YouTube लाइव शेड्यूल्ड है पर शुरू नहीं हुआ, वह Not live yet दिखता है और प्रसारण शुरू होने पर रिकॉर्ड किया जा सकता है। खत्म हो चुका वीडियो अलग चीज़ है: उसकी लंबाई होती है, इसलिए उसे फ़ाइल की तरह ट्रांसक्रिप्ट किया जाता है। लिंक को Transcriptions → New में चिपकाएँ।
प्लेलिस्ट खुद बताती है कि वह कौन सी है, और हर प्लेयर इसी से फ़र्क़ पहचानता है। खत्म हो चुकी HLS प्लेलिस्ट में #EXT-X-ENDLIST टैग या #EXT-X-PLAYLIST-TYPE:VOD होता है; लाइव प्लेलिस्ट में दोनों में से कोई नहीं होता और वह बढ़ती रहती है, इसलिए प्लेयर नए सेगमेंट के लिए उसे हर कुछ सेकंड में फिर से माँगता है। DASH मैनिफ़ेस्ट प्रसारण के चलते समय type="dynamic" घोषित करता है, और रिकॉर्डिंग बन जाने पर type="static"। Icecast सर्वर से आने वाली MP3 या AAC स्ट्रीम की कोई लंबाई ही नहीं होती; वह बस कभी रुकती नहीं।
कोड से, GET /v1/transcriptions/probe?url= कुछ भी स्टोर होने से पहले यह तय कर देता है। जवाब में kind live होता है उस YouTube लाइव के लिए जो प्रसारण में है या शेड्यूल्ड है, एक बेअंत HTTP स्ट्रीम के लिए, बिना अंत वाली HLS प्लेलिस्ट के लिए, या dynamic DASH मैनिफ़ेस्ट के लिए। जिसका अंत है उसके लिए यह file होता है, और जब probe बता नहीं पाता तब unknown। जब लिंक मीडिया के रूप में नहीं खुलता, तो ok false होता है और problem एक ऐसा वाक्य होता है जिसे आप दिखा सकते हैं। probe कुछ भी नहीं रखता और उस पर प्रति मिनट 60 कॉल की सीमा है।
फ़ाइल ट्रांसक्रिप्शन चलने वाले ऑडियो से कई गुना तेज़ चलते हैं, इसलिए खत्म हो चुके एक घंटे के टेलीविज़न को एक घंटा नहीं लगता। लाइव स्ट्रीम असली समय में ट्रांसक्रिप्ट होती है, पर "असली समय" भी प्रसारण से पीछे चलता है, और यह जानना उपयोगी है कि कितना पीछे। Icecast स्ट्रीम माइक्रोफ़ोन से कुछ सेकंड बाद प्लेयर तक पहुँचती है। HLS और DASH सेगमेंट में बँटे होते हैं, और प्लेयर तब तक शुरू नहीं होता जब तक उसके पास लगभग छह-छह सेकंड के दो-तीन सेगमेंट न हों, इसलिए HLS स्ट्रीम ट्रांसक्रिप्शन के अपने विलंब से पहले ही आम तौर पर प्रसारण से 10 से 30 सेकंड पीछे होती है। जो लाइन देर से आती दिखती है, वह अक्सर स्टेशन का विलंब होता है, ट्रांसक्रिप्टर का नहीं। एक स्ट्रीम से एक रिकॉर्डिंग बनती है; डेस्क कई स्ट्रीम चलाता है, और वे सब एक ही जगह पहुँचती हैं।
हर लाइव ट्रांसक्रिप्शन, हर फ़ाइल के साथ, Transcriptions लाइब्रेरी में जाता है। हर रिकॉर्डिंग अपने ट्रांसक्रिप्ट के साथ रखी जाती है, और शब्द अंतिम होते ही उसके लिए शीर्षक और सारांश हर भाषा में लिखा जाता है। बोली जाने वाली भाषा अपने-आप पहचानी जाती है, और वक्ता किसी भी समय भाषा बदल सकते हैं। जहाँ आप भाषा जानते हैं, उसका नाम देने से ट्रांसक्रिप्शन की गुणवत्ता बेहतर होती है।
लाइब्रेरी भाषाओं के पार खोजी जा सकती है: हर ट्रांसक्रिप्ट खोजें, चाहे उसकी भाषा कुछ भी हो, और नतीजे अपनी भाषा में पाएँ। जो रिपोर्टर सिर्फ़ अंग्रेज़ी पढ़ता है, वह रोमानियाई भाषा के स्टेशन के सुबह के शो में किसी मंत्री का नाम अंग्रेज़ी में खोज सकता है, और हर नतीजा उस ट्रांसक्रिप्शन और उस समय के साथ पढ़ सकता है जब वह बोला गया था। तीन स्टेशनों पर उस मंत्री का नाम तीन तरह से लिखा जाएगा; उसे अंग्रेज़ी में खोजने से तीनों पढ़ने की ज़रूरत नहीं पड़ती।
हर अनुवाद एक साथ अधिकतम 10 भाषाओं में किया जा सकता है, ताकि वही रिकॉर्डिंग उन सब भाषाओं में पढ़ी जा सके जिनमें डेस्क काम करता है। खोज का नतीजा तभी काम का है जब आप उसे उद्धृत कर सकें।
ट्रांसक्रिप्ट शब्दों की एक दीवार नहीं है। API का GET /v1/recordings/{id}/transcript sentences, शब्द-समय वाले segments और speakers लौटाता है, ताकि किसी उद्धरण को रिकॉर्डिंग में सेकंड तक सटीक जगह पर रखा जा सके। डैशबोर्ड लाइव रिकॉर्डिंग को ठीक वैसे ही चलाता है जैसे वह आई थी, उसके ऑडियो के साथ, ताकि उद्धृत करने से पहले कोई वाक्य सुना जा सके।
रेडियो ऑडियो के किसी भी ट्रांसक्रिप्ट पर भरोसा करने से पहले, किसी भी सिस्टम से, दो बातें जानना ज़रूरी है। स्पीच रिकग्निशन सबसे ज़्यादा उन चीज़ों पर लड़खड़ाता है जो उचित संज्ञा, जगहें और आँकड़े होते हैं, यानी वही चीज़ें जो एक रिपोर्टर खोजता है। जो नाम रिकग्नाइज़र ने पहले कभी नहीं सुना, वह सबसे मिलते-जुलते आम शब्द में बदल जाता है, और सही वर्तनी की खोज उसे नहीं पाती। उसकी जगह वह कोई खास शब्द खोजें जो उसी साँस में कहा गया हो, फिर उस नतीजे के आसपास पढ़ें। और रेडियो सिर्फ़ बोलचाल नहीं है: म्यूज़िक बेड, जिंगल और विज्ञापन ब्रेक ऐसी पंक्तियाँ बनाते हैं जो वाक्य जैसी दिखती हैं, पर होती नहीं। उद्धृत करने से पहले ऑडियो जाँच लें।
वक्ता के लेबल के तीन विकल्प हैं: बंद, चालू, या When Needed, जो तब लेबल दिखाता है जब दूसरा वक्ता सुनाई दे। आप किसी रिकॉर्डिंग में वक्ता का नाम रख सकते हैं (PUT /v1/recordings/{id}/speakers/{idx}), और एक बार किसी वक्ता की आवाज़ दर्ज हो जाए, तो आने वाली रिकॉर्डिंग उसे नाम से पहचानती हैं। जिस स्टेशन के प्रस्तोता हर सुबह लौटते हैं, उसके लिए पहले हफ़्ते के बाद लेबल "Speaker 2" नहीं रहते। उद्धरण जाँच लिया गया हो, तो फिर उसे दफ़्तर से बाहर जाना ही होता है।
ट्रांसक्रिप्शन के Export टैब पर हर फ़ॉर्मैट का न्यूज़रूम में एक काम है। SRT वीडियो एडिटर के पास जाता है, ताकि स्ट्रीम से निकली क्लिप में सबटाइटल हों; अनुवादित SRT में वे किसी दूसरी भाषा में होते हैं। Excel रिसर्चर के पास जाता है, हर वाक्य की एक पंक्ति उसके समय के साथ, ताकि उसे छाना और टिप्पणी की जा सके। PDF उस एडिटर के लिए है जो पूरा घंटा ट्रेन में पढ़ना चाहता है। JSON उसके लिए है जो डेस्क के अपने टूल लिख रहा है। API के ज़रिए एक्सपोर्ट GET /v1/recordings/{id}/export?format=srt|json|xlsx|pdf है, और अनुवादित संस्करण के लिए lang=। Download audio रिकॉर्डिंग को पैकेज के लिए MP3 के रूप में देता है।
किसी प्रसारण पर रिपोर्टिंग के लिए नज़र रखना न्यूज़रूम की आम प्रथा है। उसकी क्लिप फिर से प्रकाशित करना एक अलग काम है। वह आपकी जगह के फ़ेयर डीलिंग या फ़ेयर यूज़ नियमों से और स्टेशन की अपनी शर्तों से तय होता है, इसलिए प्रतिस्पर्धी का उद्धरण देने के डेस्क के आम नियम प्रतिस्पर्धी के ऑडियो पर भी लागू होते हैं। ऊपर की हर चीज़ डैशबोर्ड में कुछ क्लिक की है; बीस स्टेशन वाले डेस्क के लिए बेहतर है कि यह स्क्रिप्ट से चले।
पूरा रास्ता API में है, हर प्लान में शामिल है और डैशबोर्ड जितनी ही दर पर बिलिंग होती है। स्ट्रीम स्रोत वाला लाइव ट्रांसक्रिप्शन खाली बनता है, पहली रिकॉर्डिंग से शुरू होता है, और HTTP से नियंत्रित होता है। चूँकि सर्वर ऑडियो खींचता है, रिकॉर्डिंग शुरू करने पर आपको कोई स्ट्रीम नहीं मिलती जिस पर आप प्रकाशित करें: आपकी तरफ़ से ऑडियो भेजने वाला कुछ भी नहीं है।
API=$VOCALSTACK_API_ORIGIN # the origin shown in the API reference
AUTH="Authorization: Bearer $VOCALSTACK_API_KEY"
# A live transcription whose source the server pulls itself.
TX=$(jq -n '{name: "Morning show", source: {kind: "stream", url: "https://example.org/live.mp3"},
settings: {speakerLabels: "auto"}}' \
| curl -sSf "$API/v1/transcriptions/live" -H "$AUTH" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" -d @- | jq -r .id)
# Start recording: the server opens the stream now.
REC=$(curl -sSf -X POST "$API/v1/transcriptions/$TX/recordings" -H "$AUTH" \
-H "Content-Type: application/json" -d '{}' | jq -r .recording.id)
# Later: pause, then stop (a live recording must be paused before it is stopped).
curl -sSf -X POST "$API/v1/recordings/$REC/pause" -H "$AUTH"
curl -sSf -X POST "$API/v1/recordings/$REC/stop" -H "$AUTH" -H "Content-Type: application/json" -d '{}'
# Search everything that was said, in English.
curl -sSfG "$API/v1/search" -H "$AUTH" --data-urlencode "q=budget" --data-urlencode "lang=en"
Idempotency-Key से दोबारा कोशिश वाला create पहले ट्रांसक्रिप्शन को लौटाता है, दूसरा नहीं बनता। TypeScript और Python SDK इन्हीं कॉल्स को लपेटते हैं और इन्हें Developers area से डाउनलोड किया जा सकता है; API reference में हर रूट और उसके फ़्रेम मिलते हैं।
एक AI एजेंट बिना किसी कोड के भी लाइब्रेरी संभाल सकता है। /v1/mcp पर MCP सर्वर एजेंट को टूल्स का एक सेट देता है; उनमें से चार यहाँ मायने रखते हैं। search_transcriptions खाते के सभी ट्रांसक्रिप्शन में, किसी भी भाषा में, जो कहा गया उसे खोजता है और मिलती पंक्तियाँ लौटाता है, हर एक उस ट्रांसक्रिप्शन और उस समय के साथ जब वह कही गई थी। get_summary रिकॉर्डिंग का सारांश लौटाता है। transcribe_url और wait_for_transcription किसी खत्म हो चुके वीडियो या रिकॉर्डिंग के लिंक को ट्रांसक्रिप्ट करते हैं और नतीजे का इंतज़ार करते हैं। लाइव स्ट्रीम का मॉनिटर शुरू करना डैशबोर्ड या ऊपर वाले API के पास ही रहता है। Claude Code, Cursor या किसी भी Streamable HTTP क्लाइंट के लिए सेटअप Developers → Connect an AI agent के अंतर्गत है; MCP लेख इसे चरण-दर-चरण समझाता है। इस स्क्रिप्ट को चलाने की लागत अंतिम सवाल है।
लाइव ट्रांसक्रिप्शन $0.60 प्रति घंटा ऑडियो है, जो प्रति सेकंड बिल होता है ($0.01 प्रति मिनट), और हर ट्रांसक्रिप्शन के लिए न्यूनतम 5 मिनट। लाइव फ़ाइल से महँगा है, क्योंकि वह होते-होते ट्रांसक्रिप्ट होता है, जितनी देर वह चलता है। फ़ाइलें $0.20 प्रति घंटा ($0.0033 प्रति मिनट; 30 मिनट की फ़ाइल $0.10)। दरें हर प्लान और API में एक जैसी हैं, और अनुवाद, सारांश, वक्ता के नाम, ब्रॉडकास्टिंग और शेयरिंग बिल में कुछ नहीं जोड़ते।
रोज़ आठ घंटे, हफ़्ते में पाँच दिन निगरानी वाला एक स्टेशन 40 लाइव घंटे है: $24 प्रति हफ़्ता, चाहे डेस्क उसे किन भी भाषाओं में पढ़ता हो।
निगरानी के लिए मायने रखने वाली सीमाएँ प्लान के हिसाब से अलग हैं। हर सीमा Pay as you go, Premium, Enterprise के क्रम में दी गई है:
नए खाते को $5 का साइनअप क्रेडिट मिलता है और कार्ड की ज़रूरत नहीं, जो इसे आज़माने के लिए आठ घंटे से ज़्यादा की लाइव निगरानी के बराबर है। पूरी तालिका pricing page पर है।