Skip to content

Speech to text

Transcripts of audio files, with word and segment timestamps, on OpenAI's transcription route.

Transcribe a file

POST /v1/audio/transcriptions takes a multipart upload of an audio file and a model, and returns the transcript. The request shape is OpenAI's, so OpenAI SDKs work with the base URL changed. The example makes a clip with Kokoro, then transcribes the clip with Parakeet.

curl

curl https://api.albastov.com/v1/audio/speech \
  -H "Authorization: Bearer $ALBASTOV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "kokoro", "voice": "af_heart", "input": "The river bends past the old mill."}' \
  --output river.mp3

curl https://api.albastov.com/v1/audio/transcriptions \
  -H "Authorization: Bearer $ALBASTOV_API_KEY" \
  -F model=parakeet \
  -F [email protected]

Python

import os
from openai import OpenAI

client = OpenAI(base_url="https://api.albastov.com/v1", api_key=os.environ["ALBASTOV_API_KEY"])

with client.audio.speech.with_streaming_response.create(
    model="kokoro", voice="af_heart", input="The river bends past the old mill."
) as res:
    res.stream_to_file("river.mp3")

with open("river.mp3", "rb") as f:
    print(client.audio.transcriptions.create(model="parakeet", file=f).text)

JavaScript

const base = "https://api.albastov.com/v1";
const auth = { Authorization: `Bearer ${process.env.ALBASTOV_API_KEY}` };

const speech = await fetch(`${base}/audio/speech`, {
  method: "POST",
  headers: { ...auth, "Content-Type": "application/json" },
  body: JSON.stringify({ model: "kokoro", voice: "af_heart", input: "The river bends past the old mill." }),
});
if (!speech.ok) throw new Error(`${speech.status} ${await speech.text()}`);

const form = new FormData();
form.append("model", "parakeet");
form.append("file", await speech.blob(), "river.mp3");
const res = await fetch(`${base}/audio/transcriptions`, { method: "POST", headers: auth, body: form });
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log((await res.json()).text);
Response
{"text": "The river bends past the old mill."}

The file is WAV, MP3, M4A, Ogg, WebM or FLAC, 20 MB at most. The key needs the media:generate scope.

Pick a model

NeedModel
Transcripts with punctuation in 25 European languagesParakeet

Parakeet detects the language itself; language is a hint. A sample transcript: Audio models.

Word timestamps

response_format=verbose_json returns the transcript with timings. timestamp_granularities[] picks segment (the default), word, or both.

curl

curl https://api.albastov.com/v1/audio/speech \
  -H "Authorization: Bearer $ALBASTOV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "kokoro", "voice": "af_heart", "input": "The river bends past the old mill."}' \
  --output river.mp3

curl https://api.albastov.com/v1/audio/transcriptions \
  -H "Authorization: Bearer $ALBASTOV_API_KEY" \
  -F model=parakeet \
  -F [email protected] \
  -F response_format=verbose_json \
  -F "timestamp_granularities[]=word"

Python

import os
from openai import OpenAI

client = OpenAI(base_url="https://api.albastov.com/v1", api_key=os.environ["ALBASTOV_API_KEY"])

with client.audio.speech.with_streaming_response.create(
    model="kokoro", voice="af_heart", input="The river bends past the old mill."
) as res:
    res.stream_to_file("river.mp3")

with open("river.mp3", "rb") as f:
    t = client.audio.transcriptions.create(
        model="parakeet", file=f, response_format="verbose_json", timestamp_granularities=["word"]
    )
for w in t.words:
    print(f"{w.start:6.2f} {w.end:6.2f} {w.word}")
Response
{
  "task": "transcribe",
  "language": "en",
  "duration": 2.36,
  "text": "The river bends past the old mill.",
  "words": [
    {"word": "The", "start": 0, "end": 0.24},
    {"word": "river", "start": 0.24, "end": 0.56}
  ]
}

The words list above is shortened to two entries. With segment, segments lists each segment with start, end and text.

Response formats

response_formatContent typeBody
jsonapplication/json{"text": "..."}; the default
texttext/plainThe transcript and a newline
verbose_jsonapplication/jsontask, language, duration, text, and segments or words

OpenAI model name

whisper-1 runs as parakeet, so code written for OpenAI transcribes without changes. prompt and temperature are accepted and ignored; stream takes false only, because the transcript arrives whole.

Transcription jobs

POST /v1/media/jobs with kind transcription transcribes a file already on the account, or an https:// URL, without a second upload. params.audio takes an upload up_…, a job result job:<id>/<i>, a saved voice vo_… or an https:// URL; params.timestamps true adds word timings. The result entry carries the whole transcript in text. The example transcribes the clip of the shared voice vo_hVAiUiqrYyvgKKkBvgzHnh.

curl

curl "https://api.albastov.com/v1/media/jobs?wait=30" \
  -H "Authorization: Bearer $ALBASTOV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"kind": "transcription", "model": "parakeet", "params": {"audio": "vo_hVAiUiqrYyvgKKkBvgzHnh"}}'

A transcription waits in the queue up to its queue_timeout_s, 60 s by default. On POST /v1/audio/transcriptions, a job not done in time answers 503 with code queued and job_id, as on the speech route: Waiting for a result.

Reference