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.
| Name | Type | Description |
|---|---|---|
file required | file | WAV, MP3, M4A, Ogg, WebM or FLAC audio, 20 MB at most |
model required | string | parakeet, or OpenAI's whisper-1, which runs as parakeet |
language | string | A language code as a hint, such as en or de; the model detects the language itself |
response_format | string | json (default), text or verbose_json |
timestamp_granularities[] | string | With verbose_json: segment (default), word, or both as two fields |
prompt | string | Accepted and ignored |
temperature | number | Accepted and ignored |
stream | string | false only: the transcript arrives whole |
Transcription models and each model's parameters: GET /v1/media/models?kind=transcription
(Media jobs reference).
Headers
| Name | Description |
|---|---|
Idempotency-Key | Request. 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-Timeout | Request. 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-Id | Response. The media job mj_… that made the transcript |
X-Queue-Timeout | Response. The job's queue timeout in seconds |
Retry-After | Response, on 503. 5 |
X-Should-Retry | Response, 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
{
"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_format | Body |
|---|---|
json | {"text": "..."} as application/json |
text | The transcript and a newline, as text/plain |
verbose_json | task, 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.
| Status | Code | Meaning |
|---|---|---|
| 400 | bad-param | A field is missing, unknown or out of range; param names the field (file, model, language, response_format, timestamp_granularities, stream, X-Queue-Timeout) |
| 400 | bad-upload | The multipart body has no boundary or did not arrive whole |
| 413 | too-large | The file is over 20 MB |
| 415 | multipart-only | The body is not multipart/form-data |
| 415 | not-audio | The file is not WAV, MP3, M4A, Ogg, WebM or FLAC |
| 429 | budget-exhausted | The account's daily media time is used up; resets_at is the next 00:00 UTC |
| 429 | too-many-open | The key has 1,000 jobs queued or running |
| 502 | The job's error code | The job failed or was cancelled; job_id names the job |
| 503 | queued | The job is still queued; job_id names the job |
| 503 | queue-timeout | The job waited past its queue timeout and expired |
Key, scope and rate-limit errors: Errors.