Working, runnable examples for uploading media to your Upstream.so library through the public API, in several languages.
Uploads use the TUS 1.0.0 resumable protocol, which isn't built into most HTTP clients — these examples show the whole flow end to end so you can copy one into your own code.
Every upload is three steps:
- Mint a ticket —
POST /api/v1/media/uploadsreturns a short-livedupload_urlandtoken. - Upload the file — send the bytes to
upload_urlwith a TUS 1.0.0 client (or the raw protocol), authenticating withBearer {token}. When it finishes, the response carries the new file's id in thestorage-idheader. - Poll until ready —
GET /api/v1/media/{id}untilis_processingisfalse.
┌──────────┐ POST /api/v1/media/uploads ┌──────────────────┐
│ your code │ ───────────────────────────────► │ studio.upstream.so│
│ │ ◄─── { upload_url, token } ────── │ (public API) │
└────┬─────┘ └──────────────────┘
│ TUS upload (Bearer token)
▼
┌──────────────────┐ ── storage-id header ──► poll GET /api/v1/media/{id}
│ e/a/i.upstream.so │ until is_processing == false
│ (upload server) │
└──────────────────┘
You need a Personal Access Token from your dashboard: Settings → API. Every example reads it from the UPSTREAM_TOKEN environment variable:
export UPSTREAM_TOKEN="your-token-here"The same token authenticates step 1 (mint) and step 3 (poll). Step 2 uses the short-lived token from the ticket — not your PAT.
upload_url already points at your account's closest region. The ticket token is valid on every regional server, so you may upload to the nearest instead:
| Region | Upload endpoint |
|---|---|
| Europe | https://e.upstream.so/api/upload/ |
| America | https://a.upstream.so/api/upload/ |
| Asia | https://i.upstream.so/api/upload/ |
To drop the upload into a folder you own, pass its UUID:
- Mint with
{"folder_id": "<uuid>"}in the body, and - Upload with a
Folder-Id: <uuid>header.
The server validates ownership and silently ignores an unknown or foreign id (the file then lands at the library root). Every example takes the folder id as an optional second argument.
The ticket token lasts 24 hours and drives many uploads — sequential or concurrent. It is single-active per account: minting a new one (here or in the web app) revokes the previous. So for a batch, mint once and reuse it — minting per file would revoke your other in-flight uploads.
| Language | Approach | Folder |
|---|---|---|
| Node.js | tus-js-client (resumable) |
node/ |
| Python | raw TUS protocol (requests) |
python/ |
| Go | raw TUS protocol (stdlib) | go/ |
| cURL / Bash | raw TUS protocol (curl + jq) |
curl/ |
Each folder has its own README with install and run instructions. They all share the same shape:
UPSTREAM_TOKEN="your-token" <run command> ./path/to/video.mp4 [folder-uuid]No file handy? The repo ships a ready-to-upload clip at sample/demo.mp4 — a ~20 MiB public-domain test pattern that uploads in 3 chunks at the demo's 8 MiB chunk size, so you can watch the resumable protocol in action:
UPSTREAM_TOKEN="your-token" python python/upload.py sample/demo.mp4Every example chunks at 8 MiB so this small sample visibly spans several PATCH requests. That's a demo value, not a recommendation — for real uploads use larger chunks (~50 MB) to cut HTTP round-trips and upload faster. All four read it from UPSTREAM_CHUNK_MB:
UPSTREAM_CHUNK_MB=50 UPSTREAM_TOKEN="your-token" python python/upload.py ./big-video.mp4Upstream.so is a cloud streaming platform — start with a 24/7 channel and expand into every way you stream, all from one account. The media you upload with these examples is available across all four products:
- 24/7 live streaming — upload your videos, build a continuous stream, and keep a cloud channel running around the clock on YouTube.
- Pre-recorded live streaming — schedule finished videos to air as live streams, so playback runs in the cloud instead of tying up a local machine.
- Live Studio — host live podcasts, interviews, and guest shows in the browser with guests, screen share, scenes, and branded overlays.
- Multistreaming — go live to YouTube, Twitch, Kick, Facebook, Rumble, and custom RTMP destinations from a single feed.
MIT — see LICENSE.