@videas/sdk is the official JavaScript/TypeScript client for the Videas
External API. It wraps every endpoint in a small, typed surface so you don’t
have to build URLs, set headers or parse responses by hand. It has no runtime
dependency and runs anywhere a global fetch is available — Node.js 18+,
Bun, Deno and modern browsers.
Installation
npm install @videas/sdk
# or: bun add @videas/sdk / yarn add @videas/sdk
Quick start
Create a client with your organization’s API key, then call any resource:
import { createVideasClient } from '@videas/sdk'
const videas = createVideasClient({ apiKey: process.env.VIDEAS_API_KEY! })
// List the "ready" videos in your organization
const page = await videas.videos.list({ status: 'ready', limit: 50 })
console.log(`${page.count} videos`)
for (const video of page.results) {
console.log(video.uid, video.name)
}
You create an API key from Settings → API keys — see Creating an API key. The key both identifies the organization the client acts on and defines the scopes it may use.
Authentication
The key you pass to createVideasClient is sent as a Bearer token on every
request. Each client is bound to one key, so you can safely create several
clients with different keys in the same process:
const orgA = createVideasClient({ apiKey: keyA })
const orgB = createVideasClient({ apiKey: keyB })
Client options: timeouts, cancellation & retries
Besides apiKey, createVideasClient also accepts:
timeout— per-request timeout in milliseconds. The request is aborted and the promise rejects when it’s exceeded. Applies to every request, including the upload byte transfer.signal— anAbortSignalthat cancels in-flight requests (shared across every request the client makes).retries— retry idempotent (GET/HEAD) requests this many extra times on network errors or429/503, with a short back-off. Mutating calls (create/update/delete/upload) are never retried.
const videas = createVideasClient({
apiKey: process.env.VIDEAS_API_KEY!,
timeout: 10_000, // 10s
retries: 2,
})
// Cancel in-flight requests with an AbortController
const controller = new AbortController()
const scoped = createVideasClient({ apiKey, signal: controller.signal })
// controller.abort() cancels any request still in flight
Working with videos
// Get one video
const video = await videas.videos.get('vid_123')
// Update editorial fields (partial — send only what changes)
await videas.videos.update('vid_123', {
name: 'Onboarding — v2',
tags: ['onboarding', 'product'],
})
// Delete (moves it to the trash)
await videas.videos.delete('vid_123')
// Player metadata + a signed playback token
const player = await videas.videos.getPlayer('vid_123')
videos.list() accepts the same filters as the REST endpoint: search,
status, tag, created_after, created_before, ordering, plus limit
(1–100) and offset for pagination.
Other resources
The client is grouped by resource; every method returns the response body directly (already parsed and typed).
// Assets — every media type (video, audio, image, document)
await videas.assets.list({ limit: 20 })
await videas.assets.get('ast_123')
// Folders inside a workspace
await videas.folders.list('ws_123')
await videas.folders.create('ws_123', { name: 'Courses' })
await videas.folders.update('ws_123', 'fld_1', { name: 'Archived courses' })
await videas.folders.delete('ws_123', 'fld_1')
// Workspaces and their embed-domain settings
await videas.workspaces.list()
await videas.workspaces.getEmbedSettings('ws_123')
await videas.workspaces.addEmbedDomain('ws_123', { domain: 'example.com' })
URL fields are absolute. Response fields like
thumbnail_urlcome back as fully-qualified URLs, resolved against the Videas API base URL — use them directly, no host to prefix.
Resolving playback URLs
Playback is a two-step flow: get a signed token from the player endpoint, then
exchange it for the actual stream URLs. The resolvePlayback helper does both
in one call:
const info = await videas.videos.resolvePlayback('vid_123')
console.log(info.hls_url) // signed, time-limited HLS URL
assets.resolvePlayback(uid) does the same for any asset type. Prefer these
helpers; if you need the token itself, run the two steps by hand:
const player = await videas.videos.getPlayer('vid_123')
const info = await videas.playback.resolve({
asset_id: 'vid_123',
playback_token: player.playback_token,
})
Uploading a file
upload.uploadFile() handles the whole resumable (TUS) flow for you: it creates
the upload session and transfers the bytes, then resolves with the created
asset metadata (including asset_uid, available immediately). It accepts a
Blob/File, an ArrayBuffer or a typed array.
Run it on your server — it uses your secret sk_ key and must not run in a
browser. For browser uploads, mint the session server-side and transfer the
bytes to the returned capability URL: see
Resumable uploads with a TUS client.
// Node.js — from disk
import { readFile } from 'node:fs/promises'
const bytes = await readFile('intro.mp4')
await videas.upload.uploadFile({
workspaceUid: 'ws_123',
filename: 'intro.mp4',
data: bytes,
contentType: 'video/mp4',
assetName: 'Onboarding intro',
parentFolderUid: 'fld_1', // optional — defaults to the workspace root
})
Large files & progress
The byte transfer is chunked automatically — 32 MiB per request by default, safely under the server’s 50 MiB per-chunk limit — so multi-gigabyte videos upload with no extra work. Tune the chunk size and follow progress:
await videas.upload.uploadFile({
workspaceUid: 'ws_123',
filename: 'lecture.mp4',
data: bytes,
chunkSize: 16 * 1024 * 1024, // optional — default 32 MiB, must stay ≤ 50 MiB
onProgress: (sent, total) => console.log(`${Math.round((sent / total) * 100)}%`),
})
Need to drive the transfer yourself? upload.createSession() exposes the
low-level session-creation call (you pass the TUS headers and stream the bytes
to the returned location manually).
Error handling
Any non-2xx response throws a typed VideasApiError — you can try/catch
instead of checking status codes by hand. It carries the HTTP status, the
machine-readable code and the parsed error body:
import { VideasApiError } from '@videas/sdk'
try {
await videas.videos.get('does-not-exist')
} catch (err) {
if (err instanceof VideasApiError) {
console.error(err.status) // 404
console.error(err.code) // "NOT_FOUND"
console.error(err.message) // human-readable message
} else {
throw err // network / unexpected error
}
}
See the REST API guide for the full list of status codes (401, 403, 404, 429, …).
TypeScript types
Every request and response type is exported from the package, so you can annotate your own code:
import type { VideoDetailOut, VideoUpdateIn } from '@videas/sdk'
const changes: VideoUpdateIn = { name: 'New title' }
const video: VideoDetailOut = await videas.videos.update('vid_123', changes)
Current limitations
- No cross-call resume yet.
upload.uploadFile()uploads in chunks, but an interrupted call restarts from the first chunk — resuming a partially-uploaded session isn’t exposed yet. For custom resume logic, drive the transfer with a TUS client againstupload.createSession()— see Upload with the REST API.
Next steps
- Creating an API key
- Using the REST API
- Try any endpoint live with Try it out in the API reference.