Uploading files
There are two ways to get a file in: a single multipart request for anything under 500 MB, and a resumable session for larger files or flaky connections. Both end with the same file object.
Single request
curl -X POST https://api.media-host.online/v1/files \
-H "Authorization: Bearer $MH_TOKEN" \
-F "file=@ep-048-final.mp3" \
-F "collection=weekly-show" \
-F "title=048 — The slow season" \
-F "published_at=2026-08-21T06:00:00Z"
The response is returned once the bytes are stored. Duration, bitrate and loudness are filled in a few seconds later by the processing worker; until then state is processing.
{
"id": "f_7Q2kd9Lm",
"collection": "weekly-show",
"filename": "ep-048-final.mp3",
"bytes": 58213904,
"content_type": "audio/mpeg",
"state": "processing",
"url": "https://cdn.media-host.online/weekly-show/ep-048-final.mp3",
"created_at": "2026-08-19T14:02:11Z"
}
Resumable upload
Create a session, then send chunks with Content-Range. Chunks can be any size from 5 MB up; we use 32 MB in the CLI. If a chunk fails, resend it. If you lose the session ID, GET /v1/uploads lists sessions open on your token for the last 48 hours.
# 1. open a session
curl -X POST https://api.media-host.online/v1/uploads \
-H "Authorization: Bearer $MH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"collection":"lectures","filename":"week-07.mp4","bytes":2147483648}'
# → { "upload_id": "up_K3m9...", "chunk_size_hint": 33554432 }
# 2. send chunks (repeat; order does not matter)
curl -X PUT https://api.media-host.online/v1/uploads/up_K3m9.../chunk \
-H "Authorization: Bearer $MH_TOKEN" \
-H "Content-Range: bytes 0-33554431/2147483648" \
--data-binary @part-000
# 3. finish
curl -X POST https://api.media-host.online/v1/uploads/up_K3m9.../complete \
-H "Authorization: Bearer $MH_TOKEN"
Or let the CLI handle it. It splits, retries and resumes on its own:
mh put lectures ./week-07.mp4 --title "Week 7: Signals and noise"
Metadata fields
| field | type | notes |
|---|---|---|
title | string | Used in generated feeds. Defaults to filename without extension. |
description | string | Plain text or limited HTML (p, a, b, i, br). |
published_at | RFC 3339 | Future dates keep the item out of the feed until then. |
episode, season | integer | Mapped to iTunes tags. |
private | boolean | Serve only via signed URLs. Default follows the collection. |
Processing states
processing → ready in normal cases. failed means we couldn't parse the container; the file is still stored and served, but duration is unknown and feeds will omit itunes:duration. Subscribe to the file.ready webhook rather than polling.
application/octet-stream and served with a download disposition.