Skip to content

Uploads

POST /v1/media/uploads stores an audio or image file on the account and returns its id up_….

Access: Keys with the media:generate scope, on an account with media access granted by Albastar Corp.

POST /v1/media/uploads stores an audio or image file on the key's account and returns an upload id up_…. An upload is a voice reference in voice (speech), audio (transcription jobs) and from (POST /v1/media/voices), and an image to start from in init_image (image jobs), for the account that uploaded the file only.

Parameters

Send the file as the raw request body, or as the part named file of a multipart/form-data body. The file is WAV, MP3, M4A, Ogg, WebM or FLAC audio, or a PNG, JPEG or WebP image, 20 MB at most. The type is read from the file's bytes; the Content-Type of a raw body is not used.

Request

The example downloads the reference clip of Narrator A, a synthetic voice, and uploads the clip as the raw body.

curl

curl -o reference.mp3 \
  https://api.albastov.com/v1/media/samples/reference-848c3d555461736a.mp3

curl https://api.albastov.com/v1/media/uploads \
  -H "Authorization: Bearer $ALBASTAR_API_KEY" \
  -H "Content-Type: audio/mpeg" \
  --data-binary @reference.mp3

Python

import json
import os
import urllib.request

BASE = "https://api.albastov.com/v1"
UA = {"User-Agent": "my-app/1.0"}

req = urllib.request.Request(
    f"{BASE}/media/samples/reference-848c3d555461736a.mp3", headers=UA
)
with urllib.request.urlopen(req, timeout=30) as res:
    clip = res.read()

req = urllib.request.Request(
    f"{BASE}/media/uploads",
    data=clip,
    headers={
        **UA,
        "Authorization": f"Bearer {os.environ['ALBASTAR_API_KEY']}",
        "Content-Type": "audio/mpeg",
    },
)
with urllib.request.urlopen(req, timeout=60) as res:
    print(json.load(res)["id"])

JavaScript

const base = "https://api.albastov.com/v1";

const clip = await fetch(
  `${base}/media/samples/reference-848c3d555461736a.mp3`,
);
if (!clip.ok) throw new Error(`${clip.status} ${await clip.text()}`);

const res = await fetch(`${base}/media/uploads`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ALBASTAR_API_KEY}`,
    "Content-Type": "audio/mpeg",
  },
  body: await clip.arrayBuffer(),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log((await res.json()).id);

Response

201 with the upload.

Response
{
  "id": "up_Q8mZr3kPwD5nTb7yXc2hLf",
  "content_type": "audio/mpeg",
  "bytes": 225068,
  "sha256": "848c3d555461736aac50ca9009c56e9258fb6081a7724d21216923c9049212eb",
  "created_at": "2026-11-03T04:02:58.117Z",
  "source": null
}
FieldValue
idup_ and 22 random characters
content_typeThe type read from the file's bytes, such as audio/mpeg or image/png
bytesThe file's size
sha256The file's SHA-256, 64 hex characters
created_atRFC 3339 time
sourcenull for a file sent to this route

Errors

Errors have the shape {"ok": false, "error": "too-large", "code": "too-large", "message": "..."}.

StatusCodeMeaning
400bad-uploadThe file did not arrive whole, or a multipart body has no part named file
413too-largeThe file is over 20 MB
415not-an-imageThe file is neither WAV, MP3, M4A, Ogg, WebM or FLAC audio nor a PNG, JPEG or WebP image: Upload a PNG, JPEG or WebP image, or WAV, MP3, M4A, Ogg, WebM or FLAC audio.

not-an-image answers every file of another type, audio in another format included: the code is older than audio uploads and keeps its name so that existing clients still match it. Branch on the status 415 or on the code.

Key, scope and rate-limit errors: Errors.