Skip to content

Transcriptions

POST /v1/audio/transcriptions returns the transcript of an audio file, with optional timestamps.

POST /v1/audio/transcriptions returns the transcript of an uploaded audio file. The request and response shapes are OpenAI's. The key needs the media:generate scope.

Parameters

A multipart/form-data body. A field outside the list fails with 400 bad-param.

NameTypeDescription
file requiredfileWAV, MP3, M4A, Ogg, WebM or FLAC audio, 20 MB at most
model requiredstringparakeet, or OpenAI's whisper-1, which runs as parakeet
languagestringA language code as a hint, such as en or de; the model detects the language itself
response_formatstringjson (default), text or verbose_json
timestamp_granularities[]stringWith verbose_json: segment (default), word, or both as two fields
promptstringAccepted and ignored
temperaturenumberAccepted and ignored
streamstringfalse only: the transcript arrives whole

Transcription models and each model's parameters: GET /v1/media/models?kind=transcription (Media jobs reference).

Headers

NameDescription
Idempotency-KeyRequest. A request with the same key, fields and file returns the same job's transcript. Without the header, an identical request in the same 10-minute clock window returns the first request's job; a job that failed, expired or was cancelled runs again
X-Queue-TimeoutRequest. Seconds the job may wait in the queue, a whole number of 1 or more; a value above the kind's queue timeout is lowered to the queue timeout
X-Job-IdResponse. The media job mj_… that made the transcript
X-Queue-TimeoutResponse. The job's queue timeout in seconds
Retry-AfterResponse, on 503. 5
X-Should-RetryResponse, on 503 queued and 503 queue-timeout. false: OpenAI SDKs do not repeat the request

Request

The example makes a clip with Kokoro, then transcribes the clip with word timestamps.

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"]
    )
print(t.text, len(t.words))

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");
form.append("response_format", "verbose_json");
form.append("timestamp_granularities[]", "word");
const res = await fetch(`${base}/audio/transcriptions`, { method: "POST", headers: auth, body: form });
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const t = await res.json();
console.log(t.text, t.words.length);

Response

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.

response_formatBody
json{"text": "..."} as application/json
textThe transcript and a newline, as text/plain
verbose_jsontask, language, duration in seconds, text; segments unless only word was asked for; words when word was asked for

A segment has id, start, end and text, plus OpenAI's seek, tokens, temperature, avg_logprob, compression_ratio and no_speech_prob, which are always 0 or empty. A word has word, start and end in seconds.

The request waits for its job up to the job's queue timeout while the job is queued (60 s for transcription by default), and at most 85 s from arrival. An account holds 16 waiting requests at most; past that, the route answers 503 queued at once.

Errors

Errors use OpenAI's shape, {"error": {"message", "type", "param", "code"}}, plus job_id when a job exists. Branch on code.

StatusCodeMeaning
400bad-paramA field is missing, unknown or out of range; param names the field (file, model, language, response_format, timestamp_granularities, stream, X-Queue-Timeout)
400bad-uploadThe multipart body has no boundary or did not arrive whole
413too-largeThe file is over 20 MB
415multipart-onlyThe body is not multipart/form-data
415not-audioThe file is not WAV, MP3, M4A, Ogg, WebM or FLAC
429budget-exhaustedThe account's daily media time is used up; resets_at is the next 00:00 UTC
429too-many-openThe key has 1,000 jobs queued or running
502The job's error codeThe job failed or was cancelled; job_id names the job
503queuedThe job is still queued; job_id names the job
503queue-timeoutThe job waited past its queue timeout and expired

Key, scope and rate-limit errors: Errors.