Skip to main content

JavaScript / TypeScript SDK

Install and use the official @videas/sdk client to call the Videas External API from Node.js or the browser, with full TypeScript types.

@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 — an AbortSignal that cancels in-flight requests (shared across every request the client makes).
  • retries — retry idempotent (GET/HEAD) requests this many extra times on network errors or 429/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_url come 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 against upload.createSession() — see Upload with the REST API.

Next steps