All API docs

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

bash
curl -X POST https://notevibes.com/api/stt/create \
  -H "Authorization: Bearer nvb_your_api_key" \
  -F "[email protected]"

2. Poll for completion

bash
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

POST
/api/stt/create

Upload an audio file as multipart/form-data. Returns immediately with a job ID — transcription runs asynchronously.

Form Fields

FieldTypeDescription
file*fileThe audio file. mp3, wav, m4a, aac, ogg, opus, flac, mp4, webm, amr.
languagestringBCP-47 code (e.g. "en-US", "sq-AL") or "auto" to detect. Default: "auto".
modestring"immediate" (default, processed right away) or "dynamic" (cheaper, up to 24h turnaround).
wordTimestampsbooleanInclude the per-word array in the result. "false" returns transcript + segments only. Default: true.
labelstringOptional tag echoed back on the status response — handy for correlating jobs with your own records.

Example

bash
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

json
{
  "jobId": "abc123def456",
  "status": "processing"
}

Error Responses

StatusReason
400Missing file, or unsupported audio type
401Invalid or missing API key
402Insufficient credits
403API key lacks write permission

Get Job Status

GET
/api/stt/jobs/:jobId

Poll to track progress. Recommended interval: every 3–5 seconds.

Response (Processing)

json
{ "jobId": "abc123def456", "status": "processing" }

Response (Completed)

json
{
  "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:

json
// 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)

json
{ "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.