Üleslaadimine tagastab töö, mitte transkripti. Lisa igale loomisele Idempotency-Key, et uuesti proovimine ei saaks kunagi luua ega arveldada teist transkribeerimist; seejärel küsi töö olekut, kuni see on valmis, ja loe lauseid, sõnu või eksporti samast id-st.
Faili transkribeerimine on VocalStacki API kõige lihtsam toiming ja sellel on iga asünkroonse töö kuju: laadi üles, oota, loe. Üleslaadimine vastab kohe transkriptsiooni ja selle tööga, aga sõnu veel ei ole. Kui sinu programm tööd küsib, transkribeerib server faili mitu korda kiiremini, kui heli kestab. Kui olek on done, loed transkripti lausetena koos kõnelejate ja ajastustega või eksportid selle ühes neljast failivormingust, kas kõneldud keeles või tõlgituna.
Kõik selle kujuga seotu kandub üle igale partii-API-le: loomine, mis tagastab ID, olek, mida küsid, ja tulemus, mille hiljem laadid. Ainult väljade nimed on VocalStacki omad, ning see õpetus käib läbi TypeScripti ja Pythoni SDK ning kohati HTTP-päringute.
Ava töölaual Developers → API keys ja loo võti õigustega Read ja Write. Võti kuvatakse ainult üks kord; kopeeri see keskkonnamuutujasse:
export VOCALSTACK_API_KEY="<your API key>"
Võti on mõeldud serveritele ja skriptidele. Kõik, mis saadetakse brauserisse, saadetakse koos võtmega, seega hoia see lehe koodist ja URL-idest eemal, sest URL-id satuvad logidesse. Brauserist voogedastamiseks loob sinu server selle jaoks reaalajas sessiooni; see on teine õpetus.
Iga päring autendib võtmega bearer-tokenina:
Authorization: Bearer vs_key_...
Kui võti on keskkonnas, on järgmine samm klient, mis seda saadab.
Kahe SDK-d on. Igal neist on üks klient, VocalStack, mille meetodid on rühmitatud ressursi järgi: vs.transcriptions, vs.jobs, vs.recordings, vs.realtime ja veel mõned. TypeScripti SDK töötab Node 20 või uuemaga ja brauserites; Pythoni SDK põhineb asyncio-l ning vajab Pythonit 3.10 või uuemat.
SDK-d ei ole npm-is ega PyPI-s. API pakub ehitusi kõigile kontodele. Paigaldamise käsud, sinu keskkonnale kohandatuna, on kiirstardi juhendis API viites paigalduse osas, ning need saadavad sinu võtme ainult API hostile. Kui sa ei soovi midagi paigaldada, vajavad allpool olevad HTTP-päringud ainult curl-i ja jq-d.
Mida iganes valisid, esimene päring on üleslaadimine.
POST /v1/transcriptions võtab faili kaasa kui multipart/form-data: osa file ja valikuline osa options, mis sisaldab JSON-it. Vastuseks tuleb 201 koos transkriptsiooniga. Selle job.recordingId on failist tehtud salvestise id ja kõik järgnevad päringud adresseeritakse selle id järgi.
Esimesel käivitamisel on tähtsad kaks seadet, mõlemad lahtris options.settings:
spokenLanguage: kood, näiteks en. Kui jätad selle välja, tuvastatakse keel automaatselt. Selle nimetamine parandab täpsust mürarikastel salvestistel ja tugeva aktsendi korral.speakerLabels: auto, on või off. Väärtusega on kannab iga lause kõneleja numbrit; väärtusega auto ilmuvad sildid alles siis, kui on kuulda teist kõnelejat.Enamik heli- ja videofaile on vastuvõetavad: mp3, wav, flac, ogg, m4a, mp4 ja teised. Video jätab alles ainult heli. Vorming ei mõjuta täpsust märkimisväärselt, sest kõnetuvastus töötab 16 kHz mono-signaaliga ja kõik teisendatakse selleks vastuvõtmisel. 48 kHz stereo WAV, kus räägib üks inimene, kulutab enamiku baidistest vaikuse kirjeldamisele kõrges kvaliteedis. See transkribeerub täpselt sama hästi kui sama kõne 64 kbps mono MP3 või Opus-failina, kuid on umbes kakskümmend korda suurem ja seetõttu kulub üleslaadimiseks kakskümmend korda rohkem aega. Kui teisendad enne üleslaadimist, on kaks seadet, mida tasub muuta: mono ja mõõdukas bitikiirus.
Saada iga üleslaadimisega Idempotency-Key ja mõista, miks. Võrgu ajalõpp ei ütle sulle midagi selle kohta, mida server tegi: ta ei pruukinud päringut üldse näha või ta võis faili salvestada ja töö järjekorda panna enne, kui vastus kadus. Pimedalt uuesti proovimine võib seega tekitada teise transkribeerimise ja teise arve. Võti lahendab selle. Kui sama päring samade andmetega korratakse 24 tunni jooksul sama võtmega, tagastab server esimese vastuse ega loo midagi uut, mis muudab „vähemalt üks kord” praktiliselt „täpselt üks kord”-iks. Reegel on seega üks võti ühe loogilise üleslaadimise kohta, genereeritud enne esimest katset ja kasutatud igal uuesti proovimisel. Iga katse jaoks uus võti teeks iga uuesti proovimise uueks üleslaadimiseks ja päis ei kaitseks siis midagi.
import { VocalStack } from '@vocalstack/sdk';
const vs = new VocalStack({ baseUrl: '{{api}}', apiKey: process.env.VOCALSTACK_API_KEY });
const uploadKey = crypto.randomUUID(); // one key for this upload, however many attempts it takes
const tx = await vs.transcriptions.create(
{ file: './talk.mp3', name: 'Talk', settings: { spokenLanguage: 'en', speakerLabels: 'on' } },
{ idempotencyKey: uploadKey },
);
const recordingId = tx.job!.recordingId;
import asyncio
import os
import uuid
from vocalstack import VocalStack
async def main() -> None:
upload_key = str(uuid.uuid4()) # one key for this upload, however many attempts it takes
async with VocalStack("{{api}}", api_key=os.environ["VOCALSTACK_API_KEY"]) as vs:
tx = await vs.transcriptions.create(
file="./talk.mp3",
name="Talk",
settings={"spokenLanguage": "en", "speakerLabels": "on"},
idempotency_key=upload_key,
)
assert tx.job is not None
recording_id = tx.job.recording_id
Sama päring curl-iga:
API={{api}}
AUTH="Authorization: Bearer $VOCALSTACK_API_KEY"
UPLOAD_KEY=$(uuidgen)
REC=$(curl -sSf "$API/v1/transcriptions" -H "$AUTH" -H "Idempotency-Key: $UPLOAD_KEY" \
-F file=@talk.mp3 \
-F 'options={"name":"Talk","settings":{"spokenLanguage":"en","speakerLabels":"on"}}' \
| jq -r .job.recordingId)
201 tuleb tagasi enne, kui ühtegi sõna on transkribeeritud; sa saad töö ID, nii et järgmine samm on selle ootamine.
Töö liigub olekute queued → transcribing → done vahel või lõpeb olekus failed, millega kaasneb error-teade ja masinloetav errorCode, kui põhjus on masinloetav. Neljas olek, cropping, tähendab, et töö on seisma jäetud, kuni keegi valib salvestise lõigu, mida transkribeerida, nagu teeb töölaua lõikamissamm. Tavaline üleslaadimine sinna ei jõua, kuigi jobs.wait tagastab ka siis, kui töö sinna jõuab. Loe GET /v1/recordings/{id}/job-i seni, kuni olek on done või failed.
Webhooke ei ole, ja see on teadlik kompromiss. Webhook vajab sinu poolel avalikku HTTPS-lõpp-punkti, allkirjakontrolli, et keegi teine seda ei kutsuks, ja uuesti proovimise reeglit juhuks, kui sinu lõpp-punkt on maas. See on palju torustikku töö jaoks, mis kestab minuti. Päringute tegemine ei vaja midagi sellist. Sinu skript küsib iga kahe sekundi tagant „kas juba valmis?”, nagu laps tagaistmel, ainult et erinevalt lapsest saab ta vastuseks protsendi.
Juba avatud veebileht võib selle asemel jälgida transkriptsiooni sündmuste socket-it (WS /v1/transcriptions/{id}/events, kus job.progress kaader saabub töö edenedes). Skripti jaoks on päringute tegemine õige vahend.
SDK-d teevad päringute tegemise ise: vs.jobs.wait loeb tööd iga 2 sekundi tagant ja annab iga lugemise onProgress-ile (Pythonis on_progress). Töö progress sisaldab pct-d (0 kuni 100) ja processedS-i, transkribeeritud meediasekundeid. Allpool olevad programmid prindivad rea ainult siis, kui olek või protsent muutub.
let last = '';
const job = await vs.jobs.wait(recordingId, {
onProgress: (j) => {
const line = `${j.state} ${j.progress.pct}%`;
if (line !== last) console.log((last = line));
},
});
if (job.state !== 'done') throw new Error(`transcription ${job.state}: ${job.error ?? ''}`);
last = ""
def on_progress(j) -> None:
nonlocal last
line = f"{j.state} {j.progress.pct}%"
if line != last:
last = line
print(line)
job = await vs.jobs.wait(recording_id, on_progress=on_progress)
if job.state != "done":
raise RuntimeError(f"transcription {job.state}: {job.error}")
Pane tähele, et wait ei tõsta viga olekul failed; see tagastab töö ja jätab kontrolli sinu hooleks. Kaks sekundit sobib ühe kõne jaoks. Kolmetunnise salvestise või sadade failide partii puhul pikenda intervalli (intervalMs, interval_s) või lõdvenda seda töö käigus: esimesed lugemised ütlevad sulle kõige rohkem, hilisemad kinnitavad enamasti, et server on endiselt hõivatud. Ilma SDK-ta kirjutad sama tsükli käsitsi:
while :; do
JOB=$(curl -sSf "$API/v1/recordings/$REC/job" -H "$AUTH")
STATE=$(jq -r .state <<<"$JOB")
echo "$STATE $(jq -r .progress.pct <<<"$JOB")%"
case $STATE in
done) break ;;
failed) jq -r .error <<<"$JOB" >&2; exit 1 ;;
esac
sleep 2
done
Kui olek on done, on transkript valmis.
GET /v1/recordings/{id}/transcript tagastab kogu transkripti ühe dokumendina:
sentences: see, mida inimestele näidata. Igal lausel on text, speaker ning start ja end sekundites.segments: mootori read, igaühel sõnatasemel ajastused lahtris words (igal sõnal on text, start ja end).speakers: üks kirje iga kõneleja numbri kohta, koos selle name-ga.Kõnelejad on nummerdatud 0-st. speaker väärtus -1 tähendab, et salvestise kõnelejasildid olid välja lülitatud. Lisa ?lang=es, et lugeda transkripti hispaania keelde tõlgituna; laused tulevad tagasi selles keeles.
const { sentences } = await vs.recordings.transcript(recordingId);
for (const s of sentences) console.log(s.speaker >= 0 ? `[speaker ${s.speaker}] ${s.text}` : s.text);
snapshot = await vs.recordings.transcript(recording_id)
for s in snapshot.sentences:
print(f"[speaker {s.speaker}] {s.text}" if s.speaker >= 0 else s.text)
curl -sSf "$API/v1/recordings/$REC/transcript" -H "$AUTH" \
| jq -r '.sentences[] | if .speaker >= 0 then "[speaker \(.speaker)] \(.text)" else .text end'
Kaks ajastustaset teenivad kahte eesmärki. Laused on lugemise ühik, nii et need on see, mida subtiitrid või transkriptilehekülg näitab. Sõnad on osutamise ühik, nii et karaoke-stiilis esiletõstmine, otsingutulemus või klipi lõige liigub mööda segments[].words-i. Eksportvormingud on koostatud lausetest.
GET /v1/recordings/{id}/export tagastab päris faili. format on üks väärtustest srt, json, xlsx või pdf, ja fail sisaldab kõiki parandusi. Lisa lang=, et saada tõlgitud fail: eksport on siis selles keeles, üks rida lause kohta, ja iga veel tõlkimata lause tõlgitakse enne.
SRT-d on kasulik tunda näo järgi, sest see on subtiitrivorming, mida peaaegu iga mängija toetab, ja see on lihtsalt tekst. Fail on tühjade ridadega eraldatud plokkide loend; iga plokk koosneb loendurist, ajavahemikust ja tekstist. Ajatemplid kasutavad millisekundite ees koma, prantsuse harjumust, millest vorming pole kunagi loobunud:
1
00:00:01,000 --> 00:00:04,200
Welcome, everyone. Let's get started.
Üks lause plokis on õige tera: iga plokki saab lugeda ajaga, mis see ekraanil on, ja ükski ei katke lauset pooleli. Kui salvestisel olid kõnelejasildid, algab iga ploki tekst kõneleja nime ja kooloniga.
TypeScripti SDK-s tagastab vs.recordings.export Response-i; loe selle baidid arrayBuffer()-ga. Pythonis tagastab see bytes-i.
import { writeFile } from 'node:fs/promises';
const srt = await vs.recordings.export(recordingId, 'srt');
await writeFile('talk.srt', Buffer.from(await srt.arrayBuffer()));
const pdf = await vs.recordings.export(recordingId, 'pdf', { lang: 'es' });
await writeFile('talk.es.pdf', Buffer.from(await pdf.arrayBuffer()));
srt = await vs.recordings.export(recording_id, "srt")
with open("talk.srt", "wb") as f:
f.write(srt)
pdf = await vs.recordings.export(recording_id, "pdf", lang="es")
with open("talk.es.pdf", "wb") as f:
f.write(pdf)
asyncio.run(main())
curl -sSf "$API/v1/recordings/$REC/export?format=srt" -H "$AUTH" -o talk.srt
curl -sSf "$API/v1/recordings/$REC/export?format=pdf&lang=es" -H "$AUTH" -o talk.es.pdf
Transkriptsioon on salvestatud, nii et see ilmub töölaual jaotises Transcriptions, saab jagada lingiga ning seda saab hiljem igal ajal uuesti lugeda. See on põhitsükkel: loo, oota, loe, ekspordi. Ülejäänud õpetus käsitleb variante: juba veebis olevat meediat, vigu, mida sa kohtad, ja maksumust.
Kui meedia on juba veebis, anna API-le link, ja see laeb faili ise alla: avalik http(s)-aadress (mp3, video, podcasti episood) või YouTube'i video. Päring on sama POST /v1/transcriptions, JSON-kehaga { "url": "..." } multipart-üleslaadimise asemel, ja name ning settings nagu varem. SDK-des teevad sama päringu vs.transcriptions.createFromUrl(url) (TypeScript) ja vs.transcriptions.create(url=...) (Python).
Enne midagi loomist ütleb GET /v1/transcriptions/probe?url=, mis link on. Selle kind on üks kolmest:
file: lõpuga salvestis; transkribeeri see siin.live: YouTube'i otseülekanne, lõputu raadiovoog või HLS-playlist ilma lõputa. Otselink ei lõpe kunagi, seega seda ei saa transkribeerida failina; kasuta seda selle asemel reaalajas transkribeerimise allikana.unknown: server ei saanud aru.Kui link ei avane üldse meediana (näiteks veebileht), on ok väärtusega false ja problem selgitab põhjust ühes lauses. Probe on valikuline ja on piiratud 60 päringuga minutis.
Lingitöö algab olekust fetching, enne kui see liigub olekutesse transcribing ja done. Kui allalaadimine käib, ütleb progress.phase, mida server teeb: fetching, kui baidid saabuvad (koos fetchedBytes-i ja fetchTotalBytes-iga, kui teine pool ütleb, kui palju neid on), või extracting, kui heli eraldatakse videost. progress.phasePct näitab, kui kaugel on töö selles faasis. Pikk video võtab aega, nii et küsi iga paari sekundi tagant; vs.jobs.wait võtab selleks intervalMs (Pythonis interval_s).
const probe = await vs.transcriptions.probe(url);
if (!probe.ok) throw new Error(`that link did not open: ${probe.problem}`);
if (probe.kind === 'live') throw new Error('a live link: record it with a live transcription instead');
const linkKey = crypto.randomUUID();
const tx = await vs.transcriptions.createFromUrl(url, { name: 'From a link' }, { idempotencyKey: linkKey });
const job = await vs.jobs.wait(tx.job!.recordingId, {
intervalMs: 3000,
onProgress: (j) => {
const p = j.progress;
if (j.state === 'fetching') console.log(`${p.phase ?? 'fetching'} ${p.phasePct ?? 0}%`);
else console.log(`${j.state} ${p.pct}%`);
},
});
API viite Transcribe a link juhend sisaldab täielikku programmi mõlemas keeles ja HTTP-na. Linkide allalaadimisel tekib rohkem viise ebaõnnestuda, seega siin on vead, mida sa tegelikult näed.
Iga ebaõnnestunud päring vastab ühe JSON-ümbrisega. code on string, mille järgi harutada, ja status on HTTP-staatus; message on inimestele ja requestId tugiteenusele. Lahtri data all ütleb retryable, kas sama päringu uuesti saatmine võib aidata, ja details sisaldab koodi vajalikku infot. SDK-d tõstavad ühe veatüübi, VocalStackError, millel on samad väljad.
Kolm koodi tasub käsitleda juba esimesel päeval.
402 insufficient_credit: konto krediit on otsas ja midagi ei arvestatud. details.balance on saldo dollarites ja details.topUpUrl näitab, kust krediiti lisada. Kui maksumus oli ette teada, nagu failil, mille pikkus on teada, ütleb details.required, mis see oleks olnud.429 rate_limited: oota Retry-After sekundit. Kui details.metric on creates_per_hour, oled jõudnud paketi loomismäärani (uusi transkriptsioone tunnis), mitte lühiajalise piirini; limit ja resetsAt ütlevad, mis on ülempiir ja millal see vabaneb.503 unavailable: teenus on lühidalt kättesaamatu. Saada päring uuesti pärast Retry-After-i.SDK-d proovivad 429 ja 503 uuesti pärast Retry-After-i, vaikimisi kaks korda (klientide maxRetries / max_retries). 503-t aga proovitakse uuesti ainult päringul, mida on ohutu saata kaks korda: GET või loomine, millel on Idempotency-Key. Loomine ilma selleta jääb sinu hooleks, sest SDK ei saa teada, kas esimene katse läks läbi. Selle praktiline põhjus on, miks panna võti igale üleslaadimisele.
Iga vastus kannab ka RateLimit-Limit, RateLimit-Remaining ja RateLimit-Reset päiseid, nii et hõivatud torustik saab aeglustada enne 429-d.
{
"code": "insufficient_credit",
"status": 402,
"message": "this account has no credit left; add credit to continue",
"data": {
"retryable": false,
"details": { "balance": -0.42, "required": 0.075, "currency": "usd", "plan": "payg", "topUpUrl": "https://…" }
},
"requestId": "01J…"
}
Täielik koodide tabel on lehel Errors, retries and limits. See 402 on ainus raha puudutav viga, seega vaatame, mis fail maksab.
Failid maksavad $0.20 tunni heli eest, arvestatud sekundi kaupa ($0.0033 minutis) ja 5-minutilise miinimumiga: 30-minutiline fail maksab $0.10. See on kümme senti, vähem kui kohv, mille joot, kuni edenemisriba täitub. Hind on kõigil pakettidel sama ja API kaudu sama nagu töölaual. Uuel kontol on alguses ühekordne $5 registreerimiskrediit ja kaarti pole vaja.
Kolm paketipiirangut puudutavad seda õpetust.
GET /v1/realtime/capabilities näitab väljal limits.maxUploadBytes. Kehtib väiksem neist, ja fail, mis ületab selle, lükatakse enne transkribeerimist tagasi veaga 413. Pikkade salvestiste puhul on see veel üks põhjus laadida üles tihendatud mono-fail, mitte WAV.GET /v1/billing näitab sinu paketti, saldot ja piiranguid igal ajal; täielik tabel on hinnalehel.
Edasi on järgmised sammud voogedastada heli reaalajas, kui sa ei taha faili lõppu oodata, või ühendada AI-agent sama kontoga MCP-serveri kaudu, nii et see transkribeerib ja otsib sinu eest ilma ühegi ülaltoodud koodita.