Plak de stream, niet de pagina. Het echte geluid van een zender staat op een adres dat eindigt op .mp3, .aac of .m3u8, en zodra een livetranscriptie dat heeft, hoeft niemand meer te luisteren.
Een nieuwsredactie die drie radiozenders, twee televisiestreams en een handvol YouTube-lives volgt, heeft eerst een personeelsprobleem voordat er een transcriptieprobleem is. Iemand moet luisteren en iemand moet op opnemen drukken. Een livetranscriptie met een stream als bron neemt beide taken over: geef het adres van de stream op en de server opent hem, decodeert hem en transcribeert hem terwijl hij wordt uitgezonden, zodat niemand achter een bureau hoeft te zitten als een persconferentie vroeg begint. Het lastige, blijkt, is het adres vinden.
Een livetranscriptie kan zijn audio halen uit een stream die de server zelf ophaalt: een internetradiostation, een HLS-afspeellijst (.m3u8), een DASH-manifest (.mpd) of een YouTube-live. In het Dashboard is dit een nieuwe livetranscriptie via Transcriptions → New → Live waarvan de bron een stream is in plaats van de microfoon. Het adres wordt ingesteld bij het aanmaken van de transcriptie en verandert nooit, dus een monitor wordt één keer ingesteld en gestart wanneer het station de moeite waard is om op te nemen.
Het adres moet de stream zelf zijn, niet de webpagina die hem afspeelt. De pagina van een station is HTML; het geluid dat erin wordt afgespeeld komt van ergens anders, meestal van een Icecast- of Shoutcast-server, en de speler op de pagina vindt dat adres met JavaScript nadat de pagina is geladen. Zoek op de site naar een link als "Listen", "Direct stream" of "Other players": die eindigt op .mp3, .aac, .m3u, .m3u8 of .pls. Een .pls of .m3u is ook geen audio. Het is een kort tekstbestand van een paar regels, elk met het adres van een stream; het bestaat zodat desktopspelers met één link kunnen worden gevoed, en VocalStack transcribeert de stream die erin wordt genoemd. Als je in plaats daarvan de pagina plakt, vraagt VocalStack die op, ziet dat er HTML terugkomt en laat je dat weten. Elk station verbergt zijn stream een beetje anders; de meeste verbergen hem achter een knop met "Listen", en dat is het enige op de pagina dat je niet hoeft aan te klikken.
Twee soorten links worden meteen geweigerd. Links naar pagina's die media afspelen, zoals Spotify, SoundCloud of Twitch, worden tegengehouden voordat er verbinding wordt gemaakt. Streams met kopieerbeveiliging worden ook niet ondersteund: een afspeellijst of manifest waarvoor een DRM-licentie nodig is, mislukt met een melding die dat zegt. Sommige streams weigeren ook verzoeken van buiten het land van het station, dus een adres dat op kantoor wordt afgespeeld, speelt misschien niet af vanaf een server in het buitenland. Als een stream in een browser wel werkt maar niet in VocalStack, weigert het station andere spelers; vraag het station om een adres dat andere apps mogen gebruiken. De meeste bieden een MP3-, AAC- of HLS-stream aan. Zodra je het juiste adres hebt, is de volgende vraag of het ooit eindigt.
Een livestream eindigt nooit, dus het is geen bestand. Het onderscheid is vooral belangrijk voor YouTube. Een YouTube-live die in de lucht is, hoort bij een livetranscriptie, en het Dashboard volgt hem terwijl hij wordt uitgezonden. Een YouTube-live die is ingepland maar nog niet is begonnen, wordt getoond als Not live yet en kan worden opgenomen zodra hij in de lucht is. Een afgelopen video is iets anders: die heeft een lengte, dus wordt hij getranscribeerd als bestand: plak de link in Transcriptions → New.
De afspeellijst zelf zegt welk type het is, en zo kan elke speler het verschil zien. Een afgelopen HLS-afspeellijst heeft een #EXT-X-ENDLIST-tag of #EXT-X-PLAYLIST-TYPE:VOD; een live afspeellijst heeft geen van beide en blijft groeien, dus de speler haalt hem elke paar seconden opnieuw op voor nieuwe segmenten. Een DASH-manifest heeft type="dynamic" zolang de uitzending loopt en type="static" zodra het een opname is. Een MP3- of AAC-stream van een Icecast-server heeft helemaal geen lengte; hij stopt gewoon nooit.
Vanuit code bepaalt GET /v1/transcriptions/probe?url= dit voordat er iets wordt opgeslagen. Het antwoord bevat kind: live voor een YouTube-live die in de lucht of ingepland is, een eindeloze HTTP-stream, een HLS-afspeellijst zonder einde, of een dynamisch DASH-manifest. Het is file voor alles met een einde, en unknown wanneer de probe het niet kon bepalen. Als de link niet als media opende, is ok false en is problem een zin die je kunt tonen. De probe bewaart niets en is beperkt tot 60 aanroepen per minuut.
Bestandstranscripties lopen vele malen sneller dan de audio wordt afgespeeld, dus een uur televisie dat is afgelopen kost geen uur. Een livestream wordt in realtime getranscribeerd, maar "realtime" loopt al achter op de uitzending, en het is goed om te weten hoeveel. Een Icecast-stream bereikt een speler een paar seconden na de microfoon. HLS en DASH zijn gesegmenteerd, en een speler begint pas als hij twee of drie segmenten van ongeveer zes seconden heeft; een HLS-stream loopt daarom meestal 10 tot 30 seconden achter op de uitzending voordat de transcriptie zelf ook vertraging toevoegt. Een regel die laat lijkt te komen, is meestal de vertraging van het station en niet van de transcriber. Eén stream levert één opname op; een redactie draait er meerdere, en die komen allemaal op dezelfde plek terecht.
Elke livetranscriptie komt in de Transcriptions-bibliotheek naast elk bestand. Elke opname wordt bewaard met zijn transcript, en zodra de woorden definitief zijn, worden er een titel en samenvatting voor geschreven, in elke taal. De gesproken taal wordt automatisch herkend, en sprekers mogen op elk moment van taal wisselen. Als je de taal weet, verbetert het aangeven ervan de kwaliteit van de transcriptie.
De bibliotheek is doorzoekbaar over talen heen: zoek in elk transcript, in welke taal ook, en krijg de resultaten in jouw taal. Een verslaggever die alleen Engels leest, kan de ochtendshow van het Roemeense station doorzoeken op de naam van een minister en de treffers in het Engels lezen, elk met de transcriptie waaruit het komt en het moment waarop het werd gezegd. De naam van de minister wordt op drie zenders op drie manieren gespeld; door hem in het Engels te zoeken, bespaar je het lezen van alle drie.
Een transcriptie kan tegelijk in maximaal 10 talen worden vertaald, zodat dezelfde opname in elke taal leest waarin de redactie werkt. Een zoektreffer is pas nuttig als je hem kunt citeren.
Een transcript is geen muur van tekst. De route GET /v1/recordings/{id}/transcript van de API geeft sentences, segments met woordtijden en speakers terug, zodat een citaat tot op de seconde kan worden geplaatst bij de opname. Het Dashboard speelt een live-opname precies af zoals hij binnenkwam, met de audio erbij, zodat een zin kan worden beluisterd voordat hij wordt geciteerd.
Voordat je een transcript van radio-audio vertrouwt, van welk systeem ook, zijn er twee dingen die goed om te weten zijn. Spraakherkenning struikelt het vaakst over eigennamen, plaatsnamen en cijfers, precies wat een verslaggever zoekt. Een naam die de herkenner nog nooit heeft gehoord, komt eruit als het dichtstbijzijnde gewone woord, en een zoekopdracht op de juiste spelling vindt hem dan niet. Zoek in plaats daarvan naar een opvallend woord dat in dezelfde adem is gezegd, en lees daarna de omgeving van de treffer. En radio is niet alleen spraak: muziekbedden, jingles en reclameblokken leveren regels op die op zinnen lijken maar geen zinnen zijn. Controleer de audio voordat je citeert.
Sprekerlabels hebben drie instellingen: uit, aan, of When Needed, dat labels toont zodra een tweede spreker is gehoord. Je kunt een spreker in een opname een naam geven (PUT /v1/recordings/{id}/speakers/{idx}), en zodra de stem van een spreker is ingeschreven, herkennen toekomstige opnames die persoon bij naam. Voor een zender waarvan de presentatoren elke ochtend terugkomen, houden de labels na de eerste week op "Speaker 2" te zijn. Zodra het citaat is gecontroleerd, moet het het gebouw uit.
Elk formaat op het tabblad Export van een transcriptie heeft een taak in een redactie. SRT gaat naar de videoredacteur, zodat een clip uit de stream ondertiteling krijgt; een vertaalde SRT krijgt die in een andere taal. Excel gaat naar de onderzoeker, met één rij per zin en het tijdstip ervan, om te filteren en annoteren. PDF gaat naar de redacteur die het hele uur in de trein wil lezen. JSON gaat naar wie de eigen tools van de redactie bouwt. Via de API is de export GET /v1/recordings/{id}/export?format=srt|json|xlsx|pdf, met lang= voor een vertaalde versie. Download audio geeft de opname als MP3 voor het pakket zelf.
Een uitzending monitoren voor verslaggeving is normale redactionele praktijk. Een clip ervan opnieuw publiceren is iets anders. Dat valt onder fair dealing of fair use waar je woont, en onder de eigen voorwaarden van het station, dus de gebruikelijke regels van de redactie voor het citeren van een concurrent gelden ook voor de audio van de concurrent. Alles hierboven is in het Dashboard een paar klikken; voor een redactie met twintig zenders is het beter om het te scripten.
Het hele pad zit in de API, is in elk abonnement inbegrepen en wordt tegen hetzelfde tarief gefactureerd als het Dashboard. Een livetranscriptie met een stream als bron wordt inactief aangemaakt, gestart door de eerste opname, en bestuurd via HTTP. Omdat de server de audio ophaalt, levert het starten van een opname geen stream op om naartoe te publiceren: er is aan jouw kant niets dat audio verstuurt.
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"
De Idempotency-Key zorgt ervoor dat een opnieuw verzonden create de eerste transcriptie teruggeeft in plaats van een tweede. De TypeScript- en Python-SDK's pakken dezelfde aanroepen in en worden gedownload via het Developers-gebied; de API-referentie bevat elke route en zijn frames.
Een AI-agent kan de bibliotheek bedienen zonder enige code. De MCP-server op /v1/mcp geeft de agent een reeks tools; daarvan zijn er vier relevant. search_transcriptions zoekt wat er is gezegd in alle transcripties van het account, in elke taal, en geeft de overeenkomende regels terug, elk met de transcriptie waarin het staat en het moment waarop het werd gezegd. get_summary geeft de samenvatting van een opname. transcribe_url en wait_for_transcription transcriberen een afgelopen video of de link van een opname en wachten op het resultaat. Het starten van een livestream-monitor blijft bij het Dashboard of de API hierboven. De instelling voor Claude Code, Cursor of elke Streamable HTTP-client staat onder Developers → Connect an AI agent; het MCP-artikel loopt er stap voor stap doorheen. Wat het script kost om te draaien, is de laatste vraag.
Livetranscriptie kost $0.60 per uur audio, per seconde gefactureerd ($0.01 per minuut), met een minimum van 5 minuten per transcriptie. Live kost meer dan een bestand omdat het transcript wordt gemaakt terwijl het gebeurt, zolang het loopt. Bestanden kosten $0.20 per uur ($0.0033 per minuut; een bestand van 30 minuten kost $0.10). De tarieven zijn in elk abonnement en via de API hetzelfde, en vertalingen, samenvattingen, sprekernamen, uitzenden en delen maken de rekening nooit hoger.
Een station dat acht uur per dag, vijf dagen per week wordt gemonitord, is 40 live-uren: $24 per week, in welke talen de redactie het ook leest.
De limieten die er voor monitoring toe doen, verschillen per abonnement. Elk staat in de volgorde Pay as you go, Premium, Enterprise:
Een nieuw account begint met een aanmeldtegoed van $5 zonder betaalkaart, wat meer is dan acht uur live monitoring om het uit te proberen. De volledige tabel staat op de prijspagina.