Developers · API v1

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 add meta (pagination).
  • Errors return an HTTP status and {"error": {"code": "…", "message": "…"}}.
  • Rate limit: 120 requests per minute per key. Headers X-RateLimit-Limit and X-RateLimit-Remaining tell you where you are; over the limit you get 429 with Retry-After.
  • Uploads count against your daily limit (10 per day). Videos can be up to 72 hours long; there is no file-size limit.
StatusCodeMeaning
400invalid_request, invalid_json, invalid_title, invalid_statusBad input — see the message (size must be an integer)
401unauthorizedMissing, wrong or revoked key
404not_foundNo such video (or not yours), or unknown endpoint
429rate_limited, daily_limit_reachedToo many requests / uploads
500server_errorSomething failed on our side — retry, and contact support if it persists
502encoder_unavailableTemporary — 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:

  1. Create the video with POST /videos — you get its id and an upload object (endpoint, headers, metadata).
  2. Upload the file to upload.endpoint with any tus client, sending upload.headers and upload.metadata as given, in chunks of at most upload.chunk_size bytes (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

statusMeaning
createdWaiting for the file upload
uploadedFile received, queued for processing
processingBeing transcoded and delivered to storage
readyPlayable — embed_url works
failedCould 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.stageMeaningpercent
uploadingReceiving the fileshare received
queuedWaiting for a free encoder slot—
probeReading the file—
encodeTranscoding every qualityshare of the duration done
thumbnailMaking the poster—
packagePreparing 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

FieldTypeNotes
idstringUUID
titlestring
statusstringsee Video status
durationinteger | nullseconds, once known
sizeinteger | nullbytes
embed_urlstringplayer page to put in an <iframe>; plays once ready
errorstring | nullreason, when failed
progressobject | nullstage, percent, speed (× realtime), eta_seconds — see Progress
created_atstringISO 8601
ready_atstring | nullISO 8601, when ready
uploadobjectonly 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 /videos accepts duration; /me reports max_duration_seconds (max_file_bytes is now null).
  • 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.