Getting started
Pre-recorded Speech-to-Text
Transcribe audio and video files with the RealtimeVoiceKIT API: upload or link a file, create a transcript, and poll until it completes.
Pre-recorded Speech-to-Text turns audio and video files into accurate, timestamped transcripts. You point the API at a file, it processes the audio in the background, and you fetch the finished transcript with word-level timing, confidence scores, and optional speaker labels.
Transcription is asynchronous. POST /v2/transcripts returns immediately with status: "processing", and you collect the result in one of two ways: poll GET /v2/transcripts/{id} until the status becomes completed (the simple pattern used in this quickstart), or register a webhook_url and let the API notify your server when the transcript finishes (the recommended pattern for production). Short files usually finish in seconds; long recordings take longer.
Before you begin
- An API key. Create one in your workspace under Settings, then API keys. Keys look like rtvk_...
- An audio or video file, either reachable at a public URL or available locally for upload.
- Any HTTP client. The examples below use curl, Python (requests), and JavaScript (fetch).
All requests go to https://api.realtimevoicekit.com and authenticate with a bearer header: Authorization: Bearer rtvk_your_api_key.
Choose an audio source
The create endpoint accepts exactly one audio source per request:
| Source | Field | When to use |
|---|---|---|
| Public URL | audio_url | The file is already hosted somewhere the API can fetch it. Fastest to integrate. |
| Uploaded file | upload_id | The file lives on your machine or private storage. Upload it first through the signed-URL flow, then reference the upload. |
Requests that include neither field (or both) are rejected with a 400.
Transcribe from a URL
/v2/transcriptsCreate a transcript from a public audio URL or a completed upload/v2/transcripts/{transcript_id}Fetch a transcript and its current statusThe full flow is two calls: create the transcript, then poll it until the status is completed or error.
# 1. Create the transcript
curl -s -X POST https://api.realtimevoicekit.com/v2/transcripts \
-H "Authorization: Bearer rtvk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"audio_url": "https://example.com/interview.mp3",
"model": "whisper-voicekit-best",
"speaker_labels": true
}'
# => { "id": "tr_9f2c31b8...", "status": "processing", ... }
# 2. Poll until status is completed (repeat every few seconds)
curl -s https://api.realtimevoicekit.com/v2/transcripts/tr_9f2c31b8 \
-H "Authorization: Bearer rtvk_your_api_key"Transcribe a local file
For local files, upload the bytes first through a signed URL, then create the transcript with the resulting upload_id:
/v2/uploads/signRequest a signed PUT URL for an audio or video file{signed url from the sign response}Send the raw file bytes to the signed URL/v2/uploads/{upload_id}/completeConfirm the bytes arrived and mark the upload readyThe sign request takes filename, content_type, and an optional size_bytes. Only audio and video content types are accepted, uploads can be up to 5 GiB, and the signed URL stays valid for one hour. Send the PUT with the exact headers returned by the sign response.
# 1. Request a signed upload URL
curl -s -X POST https://api.realtimevoicekit.com/v2/uploads/sign \
-H "Authorization: Bearer rtvk_your_api_key" \
-H "Content-Type: application/json" \
-d '{"filename": "meeting.mp3", "content_type": "audio/mpeg"}'
# => { "upload_id": "upload_51c0...", "url": "https://...", "headers": { "Content-Type": "audio/mpeg" }, ... }
# 2. PUT the file bytes to the returned url
curl -s -X PUT "SIGNED_URL_FROM_STEP_1" \
-H "Content-Type: audio/mpeg" \
--data-binary @meeting.mp3
# 3. Confirm the upload
curl -s -X POST https://api.realtimevoicekit.com/v2/uploads/upload_51c0/complete \
-H "Authorization: Bearer rtvk_your_api_key"
# 4. Create the transcript from the upload, then poll as usual
curl -s -X POST https://api.realtimevoicekit.com/v2/transcripts \
-H "Authorization: Bearer rtvk_your_api_key" \
-H "Content-Type: application/json" \
-d '{"upload_id": "upload_51c0", "model": "whisper-voicekit-best"}'Poll until it completes
A transcript moves through these statuses:
| Status | Meaning |
|---|---|
| queued | Accepted and waiting to start. |
| processing | The audio is being transcribed. This is the status the create call returns. |
| completed | Done. text, words, utterances, confidence, and audio_duration_sec are populated. |
| error | Transcription failed. The error field explains why. Failed transcripts are not billed. |
Poll every 3 to 5 seconds for typical files. For short clips where latency matters, use a tighter loop as shown in Sync STT. Stop polling on completed or error. In production, prefer registering a webhook (next section) so your server is told when the transcript is done instead of asking repeatedly.
Get notified with a webhook
Instead of polling, pass a webhook_url when you create the transcript. When the transcript reaches a terminal state (completed or error), the API sends an HTTP POST with a small JSON notification to that URL. This is the recommended pattern for production: no wasted polling requests, and your pipeline reacts the moment the transcript is ready.
{
"audio_url": "https://example.com/interview.mp3",
"model": "whisper-voicekit-best",
"webhook_url": "https://yourapp.com/hooks/transcripts"
}{
"event": "transcript.completed",
"transcript_id": "tr_9f2c31b8",
"status": "completed"
}The event is transcript.completed on success and transcript.error on failure. The notification is deliberately small: on receipt, call GET /v2/transcripts/{id} with your API key to fetch the full transcript (the payload itself carries no transcript text, so a leaked notification exposes nothing).
Delivery rules
- The URL must be a public http or https address. Localhost, private ranges, and internal hosts are rejected at create time with 400 and code invalid_webhook_url.
- Your endpoint has 10 seconds to respond. Return a 2xx quickly and do any heavy work afterward.
- A 5xx response or a network failure is retried, so treat deliveries as at-least-once and handle duplicates idempotently (the transcript_id plus event pair is a stable deduplication key).
- A 4xx response is treated as a permanent rejection and is not retried.
- Webhook delivery never affects the transcript itself: if your endpoint is down, the transcript still completes and polling GET /v2/transcripts/{id} still works as a fallback.
What you get back
A completed transcript contains the full text plus structured timing data. All timestamps are in milliseconds from the start of the audio.
{
"id": "tr_9f2c31b8",
"status": "completed",
"model": "whisper-voicekit-best",
"title": "interview.mp3",
"language_code": "en",
"speaker_labels": true,
"text": "Thanks for joining me today. Happy to be here.",
"words": [
{ "text": "Thanks", "start": 240, "end": 480, "confidence": 0.99, "speaker": "A" }
],
"utterances": [
{
"speaker": "A",
"text": "Thanks for joining me today.",
"start": 240,
"end": 1860,
"confidence": 0.98
}
],
"confidence": 0.97,
"audio_duration_sec": 1912,
"summary": null,
"chapters": null,
"entities": null,
"sentiment_results": null,
"content_safety": null,
"webhook_url": null,
"error": null,
"created_at": "2026-07-22T14:03:11Z",
"completed_at": "2026-07-22T14:04:02Z"
}| Field | Description |
|---|---|
| id | Transcript id, prefixed tr_. Use it for every follow-up call. |
| status | queued, processing, completed, or error. |
| model | The model that ran the job. |
| title | Your title if you sent one, otherwise the uploaded filename. |
| language_code | The language you set, or the detected language once completed. |
| speaker_labels | Whether speaker diarization was requested. |
| text | The full transcript text. |
| words | Word objects with text, start, end (ms), confidence, and speaker when diarization is on. |
| utterances | Speaker turns when speaker_labels is true; see Speaker Diarization. |
| confidence | Overall confidence between 0 and 1. |
| audio_duration_sec | Audio length in seconds. This drives billing. |
| summary | Generated summary. Null unless summarization was requested. |
| chapters | Auto-detected chapters. Null unless auto_chapters was requested. |
| entities | Detected entities. Null unless entity_detection was requested. |
| sentiment_results | Per-segment sentiment. Null unless sentiment_analysis was requested. |
| content_safety | Content safety report. Null unless content_moderation was requested. |
| webhook_url | The notification URL you registered, if any. |
| error | Failure reason when status is error. |
| created_at / completed_at | Timestamps for job creation and completion. |
The analysis fields are covered in depth on their own tabs: Speech Understanding for summaries, chapters, entities, and sentiment, and Guardrails for content safety and redaction.
Beyond the raw transcript, you can fetch sentences, paragraphs, and word search or export SRT and VTT subtitles.
Model selection
Two pre-recorded models are available. Pick per request with the model field; the default is whisper-voicekit-best.
| Model | Price | Best for |
|---|---|---|
| whisper-voicekit-best | $0.252 per audio hour | Highest accuracy. The default. Focused on a set of core languages; ideal when transcript quality matters most. |
| whisper-voicekit-fast | $0.18 per audio hour | Cost-effective with broad language coverage. Ideal for high volume, long-tail languages, and latency-sensitive short clips. |
Usage is billed per second of audio at the hourly rate. GET /v2/models returns the live catalog with rates and descriptions, and GET /v2/credits shows your remaining credit.
Request options
All options for POST /v2/transcripts:
| Field | Type | Description |
|---|---|---|
| audio_url | string | Public URL of the audio or video file. Provide this or upload_id. |
| upload_id | string | Id from the upload flow. Provide this or audio_url. |
| model | string | whisper-voicekit-best (default) or whisper-voicekit-fast. |
| language_code | string | Explicit language, e.g. "es". Omit to auto-detect. See Languages. |
| language_codes | string[] | Expected languages for mixed-language audio. See Languages. |
| speaker_labels | boolean | Label who said what. See Speaker Diarization. |
| medical_mode | boolean | Medical vocabulary tuning for en, es, de, fr. See Medical Mode. |
| keyterms_prompt | string[] | Vocabulary hints, up to 1000 terms. See Keyterms Prompting. |
| pii_redaction | boolean | Redact personal information in the output. See Redaction and Filtering. |
| profanity_filter | boolean | Mask profanity in the output. See Redaction and Filtering. |
| summarization | boolean | Generate a summary, returned on the summary field. See Speech Understanding. |
| auto_chapters | boolean | Detect chapters, returned on the chapters field. See Speech Understanding. |
| entity_detection | boolean | Detect entities, returned on the entities field. See Speech Understanding. |
| sentiment_analysis | boolean | Score sentiment per segment, returned on sentiment_results. See Speech Understanding. |
| content_moderation | boolean | Flag sensitive content, returned on content_safety. See Guardrails. |
| webhook_url | string | Public http(s) URL notified when the transcript completes or fails. See the webhooks section above. |
| title | string | Optional display title for the transcript. |
| estimated_duration_sec | integer | Optional estimated audio length; pre-checks the job against your remaining credit before it starts. |