Videos API
Upload, list, update, delete, embed, download, and comments on your videos. Playback via HLS URLs on the CDN.
The upload flow (POST /videos/upload, then PUT the file to upload.url; chunked Content-Range supported) is documented in Upload flows.
Direct upload (short form)
Two steps: create asset + upload URL, then PUT bytes to the worker URL returned in data.upload.url. Use POST /videos/upload or the alias POST /videos (identical body and response).
POST https://api.dcast.pro/api/v1/videos/upload
Authorization: Bearer {api_key}
Content-Type: application/json
{
"title": "My Video",
"fileName": "video.mp4",
"fileSize": 104857600,
"visibility": "PRIVATE"
}PUT {upload.url}
Content-Type: video/mp4
(binary file)Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /videos | List videos (page, limit, status, visibility, search) |
| GET | /videos/:id | Details and playback URLs |
| POST | /videos/upload | Canonical 2-step upload init (returns upload.url; PUT bytes there). Alias: POST /videos. |
| PUT | /uploads/:token | Canonical body upload target. Supports chunked Content-Range. HEAD = probe worker. |
| GET | /videos/:id/status | Processing status (see Status & stages below) |
| GET | /videos/:id/progress | Watch progress for resume |
| GET | /videos/:id/comments | List comments (canonical pagination); owner only |
| POST | /videos/:id/comments | Post comment — body text (or content / body / message); optional authorName |
| PUT / PATCH | /videos/:id/comments/:commentId | Edit a comment (author only) |
| DELETE | /videos/:id/comments/:commentId | Soft-delete a comment (author OR video owner) |
| POST | /videos/:id/like | Like a video (idempotent) |
| DELETE | /videos/:id/like | Remove your like (idempotent) |
| POST | /videos/:id/views | Register a view event (public, no auth required) |
| GET | /videos/:id/related | Related videos (public, anchor must be PUBLIC) |
| PATCH | /videos/:id | Update title, description, visibility, isPaid, price, requiredTierId, publishedAt (aliases: paidAccess, paidAccessPrice, scheduledAt). publishedAt accepts an ISO-8601 timestamp (future = scheduled publish, past = back-date for archive migration) or null to clear. Empty body returns 400 BAD_REQUEST. A paid mode (isPaid: true) needs price greater than 0, judged on the state after the write; otherwise 400 PRICE_REQUIRED. A video stores no tags, so tags (like currency and donationsEnabled) is refused 400 UNSUPPORTED_FIELD. |
| DELETE | /videos/:id | Delete video |
| GET | /videos/:id/embed | Embed snippet (?width=, ?height=) |
| GET | /videos/:id/download | For a processed video: JSON with format: "hls", hlsUrl and note (no MP4 file); an MP4 stream only while a worker still holds the file |
| GET | /videos/:videoId/purchases | Purchases for a video (monetization) |
Visibility
A video has one of six visibility values. PATCH /videos/:id and POST /videos/upload accept them in any case plus three aliases (unlisted and bykey → LINK_ONLY, tier → TIER_EXCLUSIVE); anything else is refused 400 INVALID_VISIBILITY. POST /videos/upload defaults to PRIVATE when visibility is omitted. The ?visibility= filter on GET /videos takes the stored names only.
| Value | Who can play | Listed on GET /channel/:username/videos |
|---|---|---|
PUBLIC | Anyone, subject to isPaid / requiredTierId. | Yes, once processed |
LINK_ONLY | Anyone with the link, subject to isPaid / requiredTierId. If the video has a passphrase, the viewer must enter it. | No |
SUBSCRIBERS | A viewer signed in to DCAST with an active subscription (any tier, free included) to this creator. | No |
TIER_EXCLUSIVE | A signed-in viewer subscribed to a tier of this creator priced at least as high as requiredTierId (which must be set). | No |
REGISTERED | A viewer holding a registration pass for this video (from the registration form). | No |
PRIVATE | Only the owner. | No |
The owner's key sees every visibility in GET /videos and on its own channel listing. How these combine with paid access, and how a viewer gets a playback pass: Recipe: paid catalog.
Status & stages
Two axes describe a video's lifecycle. Top-level status is the user-facing state your UI shows; stages is per-pipeline progress for finer-grained progress bars.
| Field | Values | Use for |
|---|---|---|
status | PENDING | UPLOADING | PROCESSING | READY | FAILED | List badges, "ready to play" gate |
stages.upload | PENDING | RUNNING | DONE | FAILED | Upload progress bar |
stages.processing | PENDING | RUNNING | DONE | FAILED | Encoding progress bar |
stages.thumbnails | Same value as stages.processing | Thumbnail readiness |
When status === "READY", all three stages are DONE. results is null or an object { "phase": "completed" | "rescued" | "encoding" | "uploading" | "failed", "recoveredFromError": boolean } — never free-form internal strings.
GET /videos/:id/download on a processed video answers JSON data.note: "Direct MP4 download is not available for finalized videos. Use the HLS streaming URL above for playback, or contact support to request a source file copy."
Comments
Comments are stored in the same chat store as the watch page (channelId = video id). Only the video owner (API key owner) can list or post via these routes.
POST https://api.dcast.pro/api/v1/videos/{videoId}/comments
Authorization: Bearer {api_key}
Content-Type: application/json
{ "text": "Great episode!", "authorName": "Support Bot" }Edit a comment
PUT https://api.dcast.pro/api/v1/videos/{videoId}/comments/{commentId}
Authorization: Bearer {api_key}
Content-Type: application/json
{ "text": "Updated text" }Only the comment's original author can edit. The response includes an editedAt ISO timestamp so consumers can render an "edited" indicator. Body accepts text, content, body, or message. Hard-capped at 10000 characters.
Status discipline: stranger probing a foreign video's comment → 404 NOT_FOUND. Video owner trying to edit someone else's comment → 403 FORBIDDEN (explicit signal that editing is author-only — to moderate, use DELETE).
Delete a comment
DELETE https://api.dcast.pro/api/v1/videos/{videoId}/comments/{commentId}
Authorization: Bearer {api_key}Soft-delete (sets deletedAt). Allowed for the comment author OR the video owner (moderation). Stranger → 404 NOT_FOUND. Returns 204 No Content on success; re-deleting an already soft-deleted comment returns 404.
Likes
Toggle a like on a video. Both endpoints are idempotent — re-posting an existing like or re-deleting a missing like collapses to a no-op (no 409, no 404 on the action itself). Each call returns the canonical totalLikes aggregate so you do not need a separate read.
POST https://api.dcast.pro/api/v1/videos/{videoId}/like
DELETE https://api.dcast.pro/api/v1/videos/{videoId}/like
Authorization: Bearer {api_key}
# Response: POST -> "liked": true, DELETE -> "liked": false
{ "success": true, "data": { "liked": true, "totalLikes": 42 } }404 NOT_FOUND when the video is missing or soft-deleted. Likes are stored on a unique (userId, videoId) index, so concurrent retries are safe.
Views (public)
Register a view event from any player — embed iframes on third-party sites, native apps, or your own pages. No authentication required; if you do send a Bearer pk_… key it is attached to the row for attribution.
POST https://api.dcast.pro/api/v1/videos/{videoId}/views
Content-Type: application/json
{
"watchDurationS": 142,
"watchedPercent": 38,
"sessionId": "abc123",
"viewerCountry": "US",
"referrer": "https://example.com/article"
}
# Response
{
"success": true,
"data": {
"videoId": "...",
"totalViews": 1024,
"yourViewRegistered": true,
"deduped": false
}
}All body fields are optional. Validation: watchDurationS 0–86400, watchedPercent 0–100, sessionId 1–64 chars matching [A-Za-z0-9_-], viewerCountry 2-letter ISO code, referrer must be a valid http(s) URL.
Deduplication (24h window): when sessionId is provided dedupe is per (videoId, sessionId); otherwise per (videoId, sha1(ip + user-agent)). A duplicate returns 200 OK with deduped: true and yourViewRegistered: false — no row is written and viewCount is not incremented.
Rate limits: 30/min per (IP, videoId), plus a cluster-wide 10000/min defence. 404 NOT_FOUND when the video is missing, soft-deleted, owned by a banned creator, or has visibility=PRIVATE.
Related videos (public)
Get videos to recommend after the anchor. Public read; no authentication required. The anchor must be visibility=PUBLIC with a non-banned creator — anything else returns 404 NOT_FOUND, so this endpoint does not leak existence of private videos.
GET https://api.dcast.pro/api/v1/videos/{videoId}/related?limit=10
# Response
{
"success": true,
"data": {
"videoId": "...",
"related": [
{
"id": "...",
"title": "Other video by same creator",
"creatorUsername": "pamela",
"creatorDisplayName": "Pamela",
"thumbnailUrl": "https://...",
"durationSec": 612,
"viewCount": 8421,
"publishedAt": "2026-05-20T12:00:00.000Z",
"matchReason": "same-creator"
}
]
}
}limit defaults to 10, max 50. Returns other PUBLIC videos by the anchor's owner, newest first, up to limit; videos of other creators are never included. matchReason is always same-creator.
Rate limit: 60/min/IP. Cache: 5 minutes public.
Embed
GET /videos/:id/embed returns an HTML snippet that loads the player iframe. By default the snippet is responsive: width=100%, aspect-ratio 16/9. Override the dimensions via query params.
# Responsive (default)
curl -s -H "Authorization: Bearer pk_YOUR_KEY_HERE" \
"https://api.dcast.pro/api/v1/videos/{videoId}/embed"
# Fixed pixel size
curl -s -H "Authorization: Bearer pk_YOUR_KEY_HERE" \
"https://api.dcast.pro/api/v1/videos/{videoId}/embed?width=854&height=480"width and height must be positive integers; max 4096. Out-of-range or non-numeric values fall back to the responsive defaults. The iframe sets frame-ancestors CSP from the account's embed allowlist — sites not in the list see a refused-to-display error.
Security & scope
pk_* keys operate on videos owned by the key holder only. Foreign video lookups on owner-only endpoints return 404 NOT_FOUND. Public endpoints (/videos/:id/views, /videos/:id/related) work for any visibility=PUBLIC video by any creator. Keys cannot delete other creators' videos, cannot promote videos to PUBLIC across accounts, and cannot mutate creatorId.
Video editor
Editor metadata and export jobs: Video editor.
