Վերբեռնումը վերադարձնում է ոչ թե տեքստ, այլ աշխատանք։ Յուրաքանչյուր ստեղծման հարցման հետ ուղարկեք Idempotency-Key, որպեսզի կրկնակի փորձը երբեք չստեղծի և չվճարեցնի երկրորդ տառադարձումը. ապա սպասեք աշխատանքի ավարտին և նույն id-ից կարդացեք նախադասությունները, բառերը կամ արտահանումը։
Ֆայլի տառադարձումը VocalStack API-ի ամենապարզ գործողությունն է, և այն ունի ցանկացած ասինխրոն աշխատանքի ձևը՝ վերբեռնել, սպասել, կարդալ։ Վերբեռնումը անմիջապես պատասխանում է տառադարձումով և իր աշխատանքով, բայց բառեր դեռ չկան։ Մինչ ձեր ծրագիրը հարցնում է աշխատանքի վիճակը, սերվերը ֆայլը տառադարձում է ձայնի նվագարկման արագությունից շատ անգամ արագ։ Երբ վիճակը done է, տեքստը կարդում եք որպես խոսնակներով ու ժամանակներով նախադասություններ, կամ արտահանում եք այն չորս ձևաչափերից մեկով՝ խոսվող լեզվով կամ թարգմանված։
Այս ձևը փոխանցվում է ցանկացած batch API-ի՝ ստեղծում, որը վերադարձնում է id, կարգավիճակ, որին հարցում եք, և արդյունք, որը հետո ստանում եք։ Միայն դաշտերի անունները յուրահատուկ են VocalStack-ին, և այս ձեռնարկը դրանք քայլ առ քայլ ցույց է տալիս TypeScript և Python SDK-ներով, իսկ տեղ-տեղ՝ HTTP կանչերով։
Բացեք Developers → API keys կառավարման վահանակում և ստեղծեք բանալի Read և Write թույլտվություններով։ Բանալին ցուցադրվում է միայն մեկ անգամ. պատճենեք այն միջավայրի փոփոխականի մեջ՝
export VOCALSTACK_API_KEY="<your API key>"
Բանալին ծառայիչների և սկրիպտների համար է։ Ամեն ինչ, որ ուղարկվում է բրաուզերին, բանալին էլ է տանում իր հետ, ուստի այն պահեք էջի կոդից և URL-ներից դուրս, որովհետև դրանք հայտնվում են մատյաններում։ Բրաուզերից հոսելու համար ձեր սերվերը դրա համար ստեղծում է realtime session-ը. դա այլ ձեռնարկ է։
Յուրաքանչյուր կանչ նույնականացվում է բանալիով՝ որպես bearer token.
Authorization: Bearer vs_key_...
Երբ բանալին ձեր միջավայրում է, հաջորդ բանը դրանով հարցում ուղարկող կլիենտն է։
Երկու SDK կա։ Յուրաքանչյուրում կա մեկ կլիենտ՝ VocalStack, որի մեթոդները խմբավորված են ռեսուրսներով՝ vs.transcriptions, vs.jobs, vs.recordings, vs.realtime և մի քանիսը ևս։ TypeScript SDK-ն աշխատում է Node 20 կամ ավելի նոր տարբերակով և բրաուզերներում, Python SDK-ն կառուցված է asyncio-ի վրա և պահանջում է Python 3.10 կամ ավելի նոր։
SDK-ները npm-ում կամ PyPI-ում չեն։ API-ն բոլոր հաշիվներին սպասարկում է build-երը։ Տեղադրման հրամանները, լրացված ձեր միջավայրի համար, գտնվում են API հղման արագ մեկնարկի՝ Տեղադրման բաժնում, և դրանք բանալին ուղարկում են միայն API հոստին։ Եթե ոչինչ տեղադրել չեք ուզում, ներքևի HTTP կանչերին բավական են curl և jq։
Ինչ էլ ընտրեք, առաջին կանչը վերբեռնումն է։
POST /v1/transcriptions-ը ֆայլը ընդունում է որպես multipart/form-data՝ file մասով և ըստ ցանկության options մասով, որը պարունակում է JSON։ Պատասխանում է 201-ով՝ տառադարձումով։ Նրա job.recordingId-ն այն ձայնագրության id-ն է, որ ստեղծվել է ֆայլից, և հետո բոլոր կանչերը հասցեագրվում են այդ id-ով։
Առաջին վազքի համար երկու կարգավորում է կարևոր, և երկուսն էլ options.settings-ի ներսում՝
spokenLanguage: կոդ, օրինակ՝ en։ Եթե բաց թողնեք, լեզուն ինքնաբերաբար որոշվում է։ Նշելը բարելավում է ճշգրտությունը աղմկոտ ձայնագրությունների և ուժեղ շեշտադրման դեպքում։speakerLabels: auto, on կամ off։ on-ի դեպքում յուրաքանչյուր նախադասություն կրում է խոսնակի համարը, auto-ի դեպքում պիտակները հայտնվում են միայն այն բանից հետո, երբ լսվի երկրորդ խոսնակը։Աուդիոյի և վիդեոյի մեծ մասն ընդունվում է՝ mp3, wav, flac, ogg, m4a, mp4 և այլ ձևաչափերով։ Վիդեոն պահում է միայն իր ձայնը։ Ձևաչափը գրեթե չի ազդում ճշգրտության վրա, քանի որ խոսքի ճանաչումն աշխատում է 16 կՀց մոնո ձայնով, և ամեն ինչ ժամանակին վերախտանվում է դրան։ 48 կՀց ստերեո WAV-ը, որտեղ մեկ մարդ է խոսում, իր բայթերի մեծ մասը ծախսում է լռությունը բարձր հավատարմությամբ նկարագրելու վրա։ Այն տառադարձվում է ճիշտ այնպես, ինչպես նույն խոսքի 64 kbps մոնո MP3-ը կամ Opus ֆայլը, բայց մոտ քսան անգամ մեծ է, ուստի և վերբեռնվում է քսան անգամ երկար։ Եթե վերբեռնումից առաջ փոխարկում եք, արժե փոխել երկու կարգավորում՝ մոնո և չափավոր բիթրեյթ։
Յուրաքանչյուր վերբեռնման հետ ուղարկեք Idempotency-Key, և հասկացեք ինչու։ Ցանցի ժամանակահատվածը (timeout) բան չի ասում այն մասին, թե սերվերը ինչ է արել. այն կարող էր երբեք չտեսնել հարցումը, կամ կարող էր արդեն պահել ֆայլը և հերթ դնել աշխատանքը, մինչև պատասխանը կորավ։ Կույր կրկնակի փորձը, հետևաբար, վտանգում է երկրորդ տառադարձումը և երկրորդ վճարումը։ Բանալին դա շտկում է։ Երբ հարցումը կրկնվում է նույն բանալիով և նույն մարմնով 24 ժամվա ընթացքում, սերվերը վերադարձնում է առաջին պատասխանը՝ առանց որևէ բան ստեղծելու, և դա «առնվազն մեկ անգամ»-ը վերածում է «արդյունավետորեն մեկ անգամի»։ Այսպիսով կանոնն է՝ մեկ բանալի մեկ տրամաբանական վերբեռնման համար, որը գեներացվում է առաջին փորձից առաջ և կրկնվում է յուրաքանչյուր կրկնակի փորձի ժամանակ։ Եթե յուրաքանչյուր փորձի համար նոր բանալի լինի, յուրաքանչյուր կրկնակի փորձ նոր վերբեռնում կլինի, և վերնագիրը ոչինչ չի պաշտպանի։
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
Նույն կանչը curl-ով՝
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-ը վերադառնում է մինչև մեկ բառի տառադարձումը. ձեռքում ունեք աշխատանքի id, ուստի հաջորդ քայլը դրա սպասումն է։
Աշխատանքն անցնում է queued → transcribing → done վիճակներով, կամ ավարտվում է failed-ով՝ error հաղորդագրությամբ և, երբ պատճառը մեքենայաբար կարդացվող է, errorCode-ով։ Չորրորդ վիճակը՝ cropping, նշանակում է, որ աշխատանքը կանգնած է, մինչև ինչ-որ մեկը ընտրի ձայնագրության այն հատվածը, որը պետք է տառադարձվի, ինչպես անում է կառավարման վահանակի կտրման քայլը։ Սովորական վերբեռնումը երբեք չի մտնում այդ վիճակ, թեև jobs.wait-ը վերադառնում է նաև այն դեպքում, երբ աշխատանքը հասնում է դրան։ Կարդացեք GET /v1/recordings/{id}/job-ը, մինչև վիճակը լինի done կամ failed։
Webhook-ներ չկան, և դա գիտակցված փոխանակում է։ Webhook-ին պետք է ձեր կողմից հանրային HTTPS վերջնակետ, ստորագրության ստուգում, որպեսզի ուրիշը չկարողանա կանչել այն, և կրկնակի փորձի քաղաքականություն՝ երբ ձեր վերջնակետը անջատված է։ Դա բավական շատ խողովակաշարային աշխատանք է մի աշխատանքի համար, որը տևում է մեկ րոպե։ Հարցումը ոչինչ դրանցից չի պահանջում։ Ձեր սկրիպտը յուրաքանչյուր երկու վայրկյանը մեկ կհարցնի՝ «արդեն ավարտվա՞ծ է», և ի տարբերություն հետևի նստատեղի երեխայի, այն տոկոս է ստանում որպես պատասխան։
Արդեն բաց գտնվող վեբ էջը կարող է փոխարենը հետևել տառադարձման իրադարձությունների socket-ին (WS /v1/transcriptions/{id}/events, job.progress ֆրեյմ՝ աշխատանքի առաջ ընթանալուն զուգընթաց)։ Սկրիպտի համար հարցումն է ճիշտ գործիքը։
SDK-ները հարցումը կատարում են ինքնուրույն. vs.jobs.wait-ը աշխատանքը կարդում է յուրաքանչյուր 2 վայրկյանը մեկ և յուրաքանչյուր ընթերցում հանձնում է onProgress-ին (Python-ում՝ on_progress)։ Աշխատանքի progress-ը պարունակում է pct (0-ից 100) և processedS՝ մինչև հիմա տառադարձված մեդիայի վայրկյանները։ Ստորև ծրագրերը տպում են տող միայն այն ժամանակ, երբ վիճակը կամ տոկոսը փոխվում է։
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}")
Նկատի ունեցեք, որ wait-ը չի նետում սխալ failed-ի դեպքում. այն վերադարձնում է աշխատանքը և ստուգումը թողնում է ձեզ։ Երկու վայրկյանը բավական է խոսքի համար։ Երեք ժամ տևողությամբ ձայնագրության կամ հարյուրավոր ֆայլերի խմբաքանակի համար մեծացրեք ընդմիջումը (intervalMs, interval_s) կամ աստիճանաբար երկարացրեք այն աշխատանքի ընթացքում. առաջին ընթերցումները ամենաշատն են ասում, մնացածները հիմնականում հաստատում են, որ սերվերը դեռ զբաղված է։ Առանց SDK-ի՝ նույն ցիկլը գրում եք ձեռքով․
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
Երբ վիճակը ցույց է տալիս done, տառադարձումը պատրաստ է։
GET /v1/recordings/{id}/transcript-ը ամբողջ տառադարձումը վերադարձնում է մեկ փաստաթղթով՝
sentences: այն, ինչ պետք է ցույց տալ մարդկանց։ Յուրաքանչյուրն ունի text, speaker, և start ու end վայրկյաններով։segments: շարժիչի տողերը, յուրաքանչյուրը բառերի մակարդակի ժամանակներով words-ում (յուրաքանչյուր բառ ունի text, start և end)։speakers: յուրաքանչյուր խոսնակի համարի համար մեկ գրառում՝ իր name-ով։Խոսնակները համարակալվում են 0-ից։ speaker-ի -1 արժեքը նշանակում է, որ այդ ձայնագրության համար խոսնակի պիտակները անջատված էին։ Ավելացրեք ?lang=es, որպեսզի կարդաք տառադարձումը իսպաներենով թարգմանված. նախադասությունները կվերադառնան այդ լեզվով։
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'
Երկու ժամանակային մակարդակը երկու աշխատանք է ծառայում։ Նախադասությունները ընթերցանության միավորն են, ուստի ենթագիրը կամ տառադարձման էջը հենց դրանք են ցույց տալիս։ Բառերը ցուցման միավորն են, ուստի karaoke ոճի ընդգծումը, որոնման հիթը կամ կլիպի կտրումը քայլում են segments[].words-ով։ Արտահանման ձևաչափերը կառուցված են նախադասությունների վրա։
GET /v1/recordings/{id}/export-ը վերադարձնում է իսկական ֆայլ։ format-ը մեկն է՝ srt, json, xlsx կամ pdf, և ֆայլը ներառում է ցանկացած ուղղում։ Թարգմանված ֆայլի համար ավելացրեք lang=. այդ դեպքում արտահանումը կարդում է այդ լեզվով, մեկ տող յուրաքանչյուր նախադասության համար, և ցանկացած չթարգմանված նախադասություն նախ թարգմանվում է։
SRT-ն արժե ճանաչել տեսքով, որովհետև դա ենթագրերի այն ձևաչափն է, որը գրեթե ամեն նվագարկիչ ընդունում է, և այն պարզ տեքստ է։ Ֆայլը ենթագրերի (cue) ցուցակ է՝ տարանջատված դատարկ տողերով, և յուրաքանչյուր cue-ն բաղկացած է համարից, ժամանակային միջակայքից և տեքստից։ Ժամանակի նշումը միլիվայրկյաններից առաջ ստորակետ է օգտագործում՝ ֆրանսիական սովորություն, որից ձևաչափը երբեք չի ազատվել․
1
00:00:01,000 --> 00:00:04,200
Welcome, everyone. Let's get started.
Մեկ նախադասություն մեկ cue-ում ճիշտ չափն է. յուրաքանչյուր cue կարդացվում է իր էկրանին մնալու ժամանակում, և ոչ մեկը չի կտրվում մտքի մեջտեղում։ Երբ ձայնագրությունն ունեցել է խոսնակի պիտակներ, յուրաքանչյուր cue-ի տեքստը սկսվում է խոսնակի անունով և երկու կետով։
TypeScript SDK-ում vs.recordings.export-ը վերադարձնում է Response. նրա բայթերը կարդացեք arrayBuffer()-ով։ Python-ում այն վերադարձնում է bytes։
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
Տառադարձումը պահվում է, ուստի այն հայտնվում է կառավարման վահանակում՝ Transcriptions-ի տակ, կարող է կիսվել հղումով և կարդացվել ցանկացած ժամանակ հետո։ Դա հիմնական շրջապտույտն է՝ ստեղծել, սպասել, կարդալ, արտահանել։ Ձեռնարկի մնացած մասը ընդգրկում է տարբերակները. առցանց արդեն առկա մեդիան, կրած սխալները և ֆայլի արժեքը։
Եթե մեդիան արդեն առցանց է, API-ին տվեք հղումը, և նա ինքը կբեռնի ֆայլը՝ հանրային http(s) հասցե (mp3, վիդեո, փոդքասթի դրվագ) կամ YouTube տեսանյութ։ Կանչը նույնն է՝ POST /v1/transcriptions, JSON մարմնով { "url": "..." }՝ multipart վերբեռնման փոխարեն, և name ու settings՝ ինչպես նախկինում։ SDK-ներում vs.transcriptions.createFromUrl(url) (TypeScript) և vs.transcriptions.create(url=...) (Python) կատարում են նույն հարցումը։
Ստեղծելուց առաջ GET /v1/transcriptions/probe?url=-ը ասում է, թե ինչ է հղումը։ Նրա kind-ը երեքից մեկն է՝
file: վերջ ունեցող ձայնագրություն. տառադարձեք այստեղ։live: YouTube ուղիղ եթեր, անվերջ ռադիոհոսք կամ վերջ չունեցող HLS ցանկ։ Ուղիղ հղումը երբեք չի ավարտվում, ուստի այն որպես ֆայլ չի կարելի տառադարձել. օգտագործեք այն որպես ուղիղ տառադարձման աղբյուր։unknown: սերվերը չկարողացավ որոշել։Երբ հղումը որպես մեդիա չի բացվում (օրինակ՝ վեբ էջ), ok-ը false է, և problem-ը մեկ նախադասությամբ բացատրում է պատճառը։ Probe-ը կամընտիր է և սահմանափակված է րոպեում 60 կանչով։
Հղումային աշխատանքը սկսվում է fetching-ով, նախքան transcribing-ը և done-ը։ Մինչ այն բեռնում է, progress.phase-ը ասում է, թե սերվերն ինչ է անում՝ fetching՝ մինչ բայթերը գալիս են (fetchedBytes-ով և fetchTotalBytes-ով, երբ մյուս կողմը ասում է, թե քանիսն են), կամ extracting՝ երբ աուդիոն հանվում է վիդեոյից։ progress.phasePct-ը ասում է, թե այդ փուլի որ մասում է աշխատանքը։ Երկար վիդեոն որոշ ժամանակ է պահանջում, ուստի հարցեք յուրաքանչյուր մի քանի վայրկյանը մեկ, և vs.jobs.wait-ը այդ համար ընդունում է intervalMs (Python-ում՝ 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 հղումում ունի ամբողջական ծրագիրը երկու լեզվով՝ TypeScript և Python, և HTTP-ով։ Հղումներից ֆայլ ստանալը ավելի շատ ձախողման ուղիներ է բերում, ուստի ահա այն սխալները, որոնց իրականում կհանդիպեք։
Յուրաքանչյուր ձախողված կանչ պատասխանում է մեկ JSON ծրարով։ code-ը այն տողն է, որով պետք է ճյուղավորել, status-ը՝ HTTP կարգավիճակը։ message-ը մարդկանց համար է, requestId-ը՝ աջակցության։ data-ի տակ retryable-ը ասում է, արդյո՞ք նույն հարցումը նորից ուղարկելը կարող է օգնել, իսկ details-ում կա այն, ինչ պետք է կոդին։ SDK-ները բարձրացնում են մեկ սխալի տեսակ՝ VocalStackError, նույն դաշտերով։
Երեք կոդ արժե մշակել առաջին օրից։
402 insufficient_credit: հաշվի կրեդիտն սպառվել է, և ոչինչ չի գանձվել։ details.balance-ը հաշվեկշիռն է դոլարով, իսկ details.topUpUrl-ը այն հասցեն է, որտեղից կարող եք կրեդիտ ավելացնել։ Երբ արժեքը նախապես հայտնի էր, ինչպես հայտնի տևողության ֆայլի դեպքում, details.required-ը ասում է, թե որքան կլիներ։429 rate_limited: սպասեք Retry-After վայրկյան։ Երբ details.metric-ը creates_per_hour է, դուք հասել եք պլանի ստեղծման արագությանը (նոր տառադարձումներ ժամում), ոչ թե պայթյունային սահմանաչափին. limit-ը և resetsAt-ը ասում են, թե որն է սահմանը և երբ է այն ազատվում։503 unavailable: ծառայությունը ժամանակավորապես անհասանելի է։ Նորից ուղարկեք հարցումը Retry-After-ից հետո։SDK-ները կրկնում են 429-ը և 503-ը Retry-After-ից հետո, լռելյայն երկու անգամ (maxRetries / max_retries կլիենտի վրա)։ Սակայն 503-ը կրկնվում է միայն այն կանչի վրա, որը անվտանգ է երկու անգամ ուղարկել՝ GET-ը կամ Idempotency-Key-ով ստեղծումը։ Առանց բանալիի ստեղծումը ձեզ է թողնված, քանի որ SDK-ն չի կարող իմանալ, արդյո՞ք առաջին փորձն անցել է։ Դա է գործնական պատճառը, որ յուրաքանչյուր վերբեռնման վրա բանալի դնեք։
Յուրաքանչյուր պատասխան նաև կրում է RateLimit-Limit, RateLimit-Remaining և RateLimit-Reset, որպեսզի զբաղված խողովակաշարը կարողանա դանդաղել մինչև 429-ը։
{
"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…"
}
Կոդերի ամբողջական աղյուսակը գտնվում է Սխալներ, կրկնակի փորձեր և սահմանաչափեր բաժնում։ Այդ 402-ը միակ սխալն է, որ կապված է փողի հետ, ուստի ահա, թե ֆայլն ինչ արժե։
Ֆայլերն արժեն $0.20 մեկ ժամ աուդիոյի համար, վճարվում են վայրկյանով ($0.0033 րոպեում) 5 րոպե նվազագույնով. 30 րոպեանոց ֆայլն արժե $0.10։ Դա տասը ցենտ է, ավելի քիչ, քան այն սուրճը, որ կխմեք, մինչև առաջընթացի գիծը լցվի։ Գինը նույնն է բոլոր պլաններում և նույնը API-ով, ինչ կառավարման վահանակում։ Նոր հաշիվը սկսվում է մեկանգամյա $5 գրանցման կրեդիտով, և քարտ պետք չէ։
Երեք պլանի սահմանաչափ է վերաբերում այս ձեռնարկին։
GET /v1/realtime/capabilities-ը ցույց է տալիս որպես limits.maxUploadBytes։ Երկուսից փոքրն է գործում, և դրանից մեծ ֆայլը մերժվում է 413-ով՝ նախքան որևէ տառադարձում։ Երկար ձայնագրության համար սա ևս մեկ պատճառ է ավելի սեղմված մոնո ֆայլ վերբեռնել, քան WAV։GET /v1/billing-ը ցանկացած պահի ցույց է տալիս ձեր պլանը, հաշվեկշիռը և սահմանները. ամբողջական աղյուսակը գտնվում է գնային էջում։
Այստեղից հաջորդ քայլերն են՝ աուդիոյի իրական ժամանակի հոսքը, երբ չեք կարող սպասել ֆայլի ավարտին, կամ AI գործակալի միացումը նույն հաշվին MCP սերվերի միջոցով, որ այն տառադարձի և որոնի ձեր փոխարեն՝ առանց վերևի կոդի։