Speech-to-Text API
Upload an audio file and get back a transcript with sentence-level segments and per-word timestamps. Automatic language detection, any file length.
Quick Start
Upload audio, poll for completion, read the transcript.
1. Upload audio
curl -X POST https://notevibes.com/api/stt/create \
-H "Authorization: Bearer nvb_your_api_key" \
-F "[email protected]"2. Poll for completion
curl https://notevibes.com/api/stt/jobs/{jobId} \
-H "Authorization: Bearer nvb_your_api_key"3. When status is "completed", fetch result.transcriptUrl for the transcript JSON
Create Transcription Job
/api/stt/createUpload an audio file as multipart/form-data. Returns immediately with a job ID — transcription runs asynchronously.
Form Fields
| Field | Type | Description |
|---|---|---|
file* | file | The audio file. mp3, wav, m4a, aac, ogg, opus, flac, mp4, webm, amr. |
language | string | BCP-47 code (e.g. "en-US", "sq-AL") or "auto" to detect. Default: "auto". |
mode | string | "immediate" (default, processed right away) or "dynamic" (cheaper, up to 24h turnaround). |
wordTimestamps | boolean | Include the per-word array in the result. "false" returns transcript + segments only. Default: true. |
label | string | Optional tag echoed back on the status response — handy for correlating jobs with your own records. |
Example
curl -X POST https://notevibes.com/api/stt/create \
-H "Authorization: Bearer nvb_your_api_key" \
-F "[email protected]" \
-F "language=auto" \
-F "wordTimestamps=true"Response
{
"jobId": "abc123def456",
"status": "processing"
}Error Responses
| Status | Reason |
|---|---|
400 | Missing file, or unsupported audio type |
401 | Invalid or missing API key |
402 | Insufficient credits |
403 | API key lacks write permission |
Get Job Status
/api/stt/jobs/:jobIdPoll to track progress. Recommended interval: every 3–5 seconds.
Response (Processing)
{ "jobId": "abc123def456", "status": "processing" }Response (Completed)
{
"jobId": "abc123def456",
"status": "completed",
"result": {
"language": "auto",
"durationSec": 92.4,
"credits": 600,
"transcriptUrl": "https://storage.googleapis.com/notevibes.com/...?X-Goog-Signature=...",
"transcriptUrlExpiresIn": 3600
}
}The transcript itself is delivered as a file — transcriptUrl is a signed link (valid 1 hour) to a JSON document. Fetch it to get the full result:
// GET transcriptUrl →
{
"text": "Mirëdita. Ky është një lajm i rëndësishëm ...",
"language": "auto",
"durationSec": 92.4,
"segments": [
{ "text": "Mirëdita.", "startSec": 0.0, "endSec": 0.9 },
{ "text": "Ky është një lajm i rëndësishëm", "startSec": 1.1, "endSec": 3.8 }
],
"words": [
{ "word": "Mirëdita", "startSec": 0.0, "endSec": 0.9 }
]
}The file always holds text + segments; words is included unless wordTimestamps=false. The link re-signs on every poll, so if it expires just re-poll /api/stt/jobs/:jobId for a fresh one.
Response (Failed)
{ "jobId": "abc123def456", "status": "failed", "error": "..." }Word vs Segment Timing
Every result carries two timing granularities, built from the same recognition pass:
- segments — sentence / pause-level chunks (broken on a pause or ~14s cap). Best for read-along and subtitles.
- words — per-word start/end times. Best for karaoke-style highlighting or precise editing.
There's no length limit — long audio is split internally and stitched back into one continuous timeline, so word timings stay accurate for a full-length recording.