StreamTB API
Upload and manage your videos from your own code. JSON over HTTPS, one API key per account.
Authentication
Create a key in Dashboard → Settings → API. It is shown once — store it like a password. Creating a new key revokes the previous one.
Send it on every request:
Authorization: Bearer stb_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
(X-API-Key: stb_… works too.) Base URL:
https://streamtb.com/api/v1
Requests & errors
- Request bodies are JSON (
Content-Type: application/json). - Successful responses wrap the result in
data; lists addmeta(pagination). - Errors return an HTTP status and
{"error": {"code": "…", "message": "…"}}. - Rate limit: 120 requests per minute per key. Headers
X-RateLimit-LimitandX-RateLimit-Remainingtell you where you are; over the limit you get429withRetry-After. - Uploads count against your daily limit (10 per day). Videos can be up to 72 hours long; there is no file-size limit.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request, invalid_json, invalid_title, invalid_status | Bad input — see the message (size must be an integer) |
| 401 | unauthorized | Missing, wrong or revoked key |
| 404 | not_found | No such video (or not yours), or unknown endpoint |
| 429 | rate_limited, daily_limit_reached | Too many requests / uploads |
| 500 | server_error | Something failed on our side — retry, and contact support if it persists |
| 502 | encoder_unavailable | Temporary — retry in a little while |
Uploading a video
Uploads use the open tus protocol, so they are resumable: a dropped connection continues where it stopped. Two steps:
- Create the video with
POST /videos— you get itsidand anuploadobject (endpoint, headers, metadata). - Upload the file to
upload.endpointwith any tus client, sendingupload.headersandupload.metadataas given, in chunks of at mostupload.chunk_sizebytes (50 MB).
Then poll GET /videos/{id} until status is ready (or failed);
while it works, progress tells you how far it is.
The upload endpoint is valid for 24 hours. Don't hard-code it — it can differ between videos.
Node.js (tus-js-client)
import fs from 'node:fs';
import * as tus from 'tus-js-client';
const API = 'https://streamtb.com/api/v1';
const KEY = process.env.STB_API_KEY;
const file = 'movie.mp4';
const size = fs.statSync(file).size;
// 1. create
const res = await fetch(`${API}/videos`, {
method: 'POST',
headers: { Authorization: `Bearer ${KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'My movie', size }),
});
const { data: video } = await res.json();
// 2. upload
await new Promise((resolve, reject) => {
new tus.Upload(fs.createReadStream(file), {
endpoint: video.upload.endpoint,
headers: video.upload.headers,
metadata: video.upload.metadata,
chunkSize: video.upload.chunk_size,
uploadSize: size,
retryDelays: [0, 3000, 10000, 30000],
onError: reject,
onSuccess: resolve,
}).start();
});
console.log('uploaded', video.id, video.embed_url);
Python (tuspy)
import os, requests
from tusclient import client
API, KEY, path = 'https://streamtb.com/api/v1', os.environ['STB_API_KEY'], 'movie.mp4'
auth = {'Authorization': f'Bearer {KEY}'}
video = requests.post(f'{API}/videos', headers=auth,
json={'title': 'My movie', 'size': os.path.getsize(path)}).json()['data']
up = video['upload']
tus = client.TusClient(up['endpoint'], headers=up['headers'])
tus.uploader(path, metadata=up['metadata'], chunk_size=up['chunk_size']).upload()
print(video['id'], video['embed_url'])
Video status
| status | Meaning |
|---|---|
created | Waiting for the file upload |
uploaded | File received, queued for processing |
processing | Being transcoded and delivered to storage |
ready | Playable — embed_url works |
failed | Could not be processed — see error (e.g. longer than 72 hours); upload again |
Progress
While a video is being handled on one of our own encoders, progress reports the current step,
refreshed about every 10 seconds. It is null when no live detail is available (some encoders
do not report it) and once the video is ready or failed — rely on status
for the outcome.
| progress.stage | Meaning | percent |
|---|---|---|
uploading | Receiving the file | share received |
queued | Waiting for a free encoder slot | — |
probe | Reading the file | — |
encode | Transcoding every quality | share of the duration done |
thumbnail | Making the poster | — |
package | Preparing the result for storage | — |
After package the video is delivered to storage (status stays processing,
progress becomes null) and then turns ready.
"progress": { "stage": "encode", "percent": 43.5, "speed": 6.2, "eta_seconds": 95 }
Endpoints
GET /me
Your account and how many uploads you have left today.
curl -H "Authorization: Bearer $STB_API_KEY" https://streamtb.com/api/v1/me
{
"data": {
"id": "…", "username": "alice", "email": "[email protected]", "display_name": "Alice",
"uploads": { "daily_limit": 10, "remaining_today": 7, "max_file_bytes": null, "max_duration_seconds": 259200 }
}
}
GET /videos
Your videos, newest first. Query: page (default 1), per_page (1–100, default 20),
status (one of the statuses above).
curl -H "Authorization: Bearer $STB_API_KEY" "https://streamtb.com/api/v1/videos?status=ready&per_page=50"
{ "data": [ { …video… } ], "meta": { "page": 1, "per_page": 50, "total": 132, "pages": 3 } }
POST /videos
Create a video and get where to upload it. Body: title (required, max 255; a trailing
.mp4-style extension is dropped), size (file size in bytes, required),
duration (seconds, optional — send it if you know it: a video over 72 hours is refused right away
instead of failing after the upload). Answers 201.
curl -X POST https://streamtb.com/api/v1/videos \
-H "Authorization: Bearer $STB_API_KEY" -H "Content-Type: application/json" \
-d '{"title": "My movie", "size": 734003200, "duration": 5400}'
{
"data": {
"id": "0f8fad5b-d9cb-469f-a165-70867728950e",
"title": "My movie",
"status": "created",
"embed_url": "https://vid-snap-player.com/embed/0f8fad5b-d9cb-469f-a165-70867728950e",
…,
"upload": {
"protocol": "tus",
"endpoint": "https://…/files/",
"headers": { "…": "…" },
"metadata": { "filetype": "video/mp4", "title": "My movie" },
"chunk_size": 52428800
}
}
}
GET /videos/{id}
One of your videos — poll this after uploading.
curl -H "Authorization: Bearer $STB_API_KEY" https://streamtb.com/api/v1/videos/0f8fad5b-d9cb-469f-a165-70867728950e
PATCH /videos/{id}
Change the title. Body: {"title": "New title"}. Returns the video.
POST /videos/{id}/uploaded
Optional: tell us the upload finished. Processing starts on its own when the file is complete; calling this only updates the status a little sooner.
DELETE /videos/{id}
Delete a video. Playback stops right away and the files are removed from storage shortly after.
Answers 204 with no body.
Video object
| Field | Type | Notes |
|---|---|---|
id | string | UUID |
title | string | |
status | string | see Video status |
duration | integer | null | seconds, once known |
size | integer | null | bytes |
embed_url | string | player page to put in an <iframe>; plays once ready |
error | string | null | reason, when failed |
progress | object | null | stage, percent, speed (× realtime), eta_seconds — see Progress |
created_at | string | ISO 8601 |
ready_at | string | null | ISO 8601, when ready |
upload | object | only in the POST /videos response |
<iframe src="https://vid-snap-player.com/embed/{id}" width="640" height="360"
frameborder="0" allow="autoplay; fullscreen" allowfullscreen></iframe>
Changelog
- 2026-09-25 — Limits: no file-size limit any more, videos up to 72 hours.
POST /videosacceptsduration;/mereportsmax_duration_seconds(max_file_bytesis nownull). - 2026-09-25 — Video object:
progress(stage, percent, speed, ETA) while a video is processed. - 2026-09-25 — v1:
/me, list / create / get / rename / delete videos, tus uploads.