Developers

The Transcoder API

Upload source files, get adaptive HLS, captions, thumbnails and downloadable MP4s, all programmatically. Everything the dashboard does is available over a simple REST API.

Base URL

bash
https://api.transcode.procd.cc/api/v1

Introduction

The API is JSON over HTTPS. Successful responses are wrapped in a data field; errors in an error field. All timestamps are ISO-8601. You authenticate with an API key created from your dashboard.

response envelope
// success
{ "data": { /* ... */ } }
// error
{ "error": { "message": "Human-readable reason" } }

Authentication

Create one or more keys in Dashboard → API keys. A key is shown once on creation, so store it securely. You can set an expiry and delete keys at any time.

Send your key as a header, either way works:

bash
# preferred
curl https://api.transcode.procd.cc/api/v1/video/user-videos \
-H "x-api-key: vtk_your_key_here"
# or as a bearer token
curl https://api.transcode.procd.cc/api/v1/video/user-videos \
-H "Authorization: Bearer vtk_your_key_here"

Keys inherit your account's permissions and limits. Revoking a key stops it working immediately.

Limits

  • · Up to 5 videos per account (lifetime).
  • · Up to 1 GB per file.
  • · Up to 5 concurrent transcodes.

Need more, or an on-prem deployment? Email hello@ayushsharma.me.

Upload a video

Uploads go straight to object storage via a presigned POST, so bytes never pass through the API. It's a two-step flow.

Step 1: request an upload URL

GET/upload/upload-videos

Query params: fileType (MIME, required), fileName (original name, optional but recommended).

bash
curl "https://api.transcode.procd.cc/api/v1/upload/upload-videos?fileType=video/mp4&fileName=clip.mp4" \
-H "x-api-key: $API_KEY"
response
{
"data": {
"url": "https://<bucket>.s3.<region>.amazonaws.com",
"fields": {
"Content-Type": "video/mp4",
"x-amz-meta-userId": "...",
"bucket": "...",
"X-Amz-Algorithm": "...",
"X-Amz-Credential": "...",
"X-Amz-Date": "...",
"key": "uploads/<userId>/video-<uuid>",
"Policy": "...",
"X-Amz-Signature": "..."
}
}
}

Step 2: POST the file to storage

Send a multipart form to url with every returned field, then the file last. A 204 means success.

bash
# echo each field from the response as -F flags, file last
curl -X POST "$UPLOAD_URL" \
-F key="$KEY" \
-F Content-Type="video/mp4" \
-F x-amz-meta-userId="$USER_ID" \
-F X-Amz-Algorithm="$ALG" \
-F X-Amz-Credential="$CRED" \
-F X-Amz-Date="$DATE" \
-F Policy="$POLICY" \
-F X-Amz-Signature="$SIG" \
-F file=@clip.mp4

Once uploaded, transcoding starts automatically. Poll the video's status (next sections).

Status lifecycle

A video moves through these states:

states
signed_url_generated -> upload URL issued, awaiting the file
uploaded -> file received, queued
transcoding -> ffmpeg is laddering renditions + captions
transcoded -> ready: HLS, MP4 downloads, captions, thumbnail
error -> processing failed

List & fetch videos

GET/video/user-videos
bash
curl https://api.transcode.procd.cc/api/v1/video/user-videos -H "x-api-key: $API_KEY"
response (one video)
{
"video_id": "0b3a927e-...",
"s3_key": "uploads/<userId>/video-<uuid>",
"original_filename": "clip.mp4",
"status": "transcoded",
"transcoded_urls": ["<userId>/video-<uuid>/1920x1080_hls/index.m3u8", "..."],
"master_playlist_url": "<userId>/video-<uuid>/master.m3u8",
"is_public": false,
"created_at": "2026-06-06T08:00:00.000Z"
}
GET/video/user-video?s3_key=
bash
curl "https://api.transcode.procd.cc/api/v1/video/user-video?s3_key=$KEY" -H "x-api-key: $API_KEY"

Thumbnails

GET/video/thumbnail?video_id=

Returns a short-lived signed image URL. The first call generates and caches it.

bash
curl "https://api.transcode.procd.cc/api/v1/video/thumbnail?video_id=$ID" -H "x-api-key: $API_KEY"
# { "data": { "url": "https://<cdn>/.../thumbnail.jpg?Expires=..." } }

Transcription

GET/video/transcription?video_id=

Captions are generated automatically. If no speech was detected, available is false.

response
{
"data": {
"available": true,
"language": "en",
"languages": ["en"],
"cues": [
{ "start": 0.0, "end": 2.4, "text": "Hello and welcome." }
]
}
}

Downloads

Get a per-quality MP4 (remuxed on demand) or a zip of all qualities. First mint a short-lived download token, then hit the download endpoint with it.

GET/video/download-token?video_id=
GET/download/video?video_id=&quality=&token=
GET/download/all?video_id=&token=
bash
# 1) mint a token (valid ~10 min)
TOKEN=$(curl -s "https://api.transcode.procd.cc/api/v1/video/download-token?video_id=$ID" \
-H "x-api-key: $API_KEY" | jq -r .data.token)
# 2) download a single quality as MP4
curl -L "https://api.transcode.procd.cc/api/v1/download/video?video_id=$ID&quality=1080p&token=$TOKEN" -o clip-1080p.mp4
# 3) or all qualities as a zip
curl -L "https://api.transcode.procd.cc/api/v1/download/all?video_id=$ID&token=$TOKEN" -o clip-all.zip

Valid quality values are the video's rendition heights, e.g. 2160p, 1080p, 720p, 360p.

Visibility & embeds

PATCH/video/visibility

Make a video public to enable embedding. Body: { "video_id": "...", "is_public": true }.

bash
curl -X PATCH https://api.transcode.procd.cc/api/v1/video/visibility \
-H "x-api-key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{"video_id":"'$ID'","is_public":true}'

Once public, embed it anywhere with an iframe:

html
<iframe
src="https://transcode.procd.cc/embed/<video_id>"
width="640" height="360"
style="border:0;border-radius:12px"
allow="autoplay; fullscreen; picture-in-picture"
allowfullscreen
></iframe>

Errors

Standard HTTP status codes; the body always carries a message.

status codes
400 Bad request / validation error
401 Missing, invalid, or expired API key
403 Forbidden (e.g. limit reached, not your resource)
404 Not found
500 Server error
example
{ "error": { "message": "API key expired" } }

Questions or higher limits? hello@ayushsharma.me