Search Documentation
Search across all documentation pages
TypeScript SDK

TypeScript SDK

@transcodely/sdk is the official TypeScript / JavaScript SDK for the Transcodely API. It runs on Node.js 20+, ships with full type definitions for every request and response, and bundles a hand-rolled Connect-RPC transport so you don’t need to install Connect-ES separately. The SDK is still pre-1.0 — breaking changes are possible on minor bumps until 1.0.0.

Install

npm install @transcodely/sdk
# or
pnpm add @transcodely/sdk
# or
yarn add @transcodely/sdk

The SDK has one dependency, @bufbuild/protobuf ^1.10.0, used for generated message types. Most package managers will install it automatically.

Authenticate

The SDK takes the API key as an explicit constructor argument. The conventional environment variable is TRANSCODELY_API_KEY.

import { Transcodely } from "@transcodely/sdk";

const client = new Transcodely({
  apiKey: process.env.TRANSCODELY_API_KEY!,
});

Never embed an API key in browser code; the SDK is server-side only.

Create your first job

Resource methods live under lazy namespaces (client.jobs, client.videos, etc.). Method arguments are typed PartialMessage<T> shapes that auto-complete from the proto schema.

import {
  Transcodely,
  OutputFormat,
  VideoCodec,
  Resolution,
} from "@transcodely/sdk";

const client = new Transcodely({ apiKey: process.env.TRANSCODELY_API_KEY! });

const job = await client.jobs.create({
  inputUrl: "https://storage.example.com/source.mp4",
  outputOriginId: "ori_x9y8z7w6v5",
  outputs: [
    {
      type: OutputFormat.HLS,
      video: [
        { codec: VideoCodec.H264, resolution: Resolution.RESOLUTION_1080P },
        { codec: VideoCodec.H264, resolution: Resolution.RESOLUTION_720P },
      ],
    },
  ],
});

console.log(job.id, job.status);

Method args use camelCase (inputUrl, outputOriginId); the SDK serializes them to snake_case (input_url, output_origin_id) on the wire automatically.

Watch a job to completion

client.jobs.watch(id) returns an AsyncIterable that emits one event per job-state change, auto-reconnecting on transient failures. Heartbeats are filtered by default. Pass an AbortSignal to cancel from your side:

import { JobStatus } from "@transcodely/sdk";

const ac = new AbortController();
setTimeout(() => ac.abort(), 10 * 60 * 1000); // 10-minute ceiling

for await (const event of client.jobs.watch(job.id, { signal: ac.signal })) {
  console.log(event.event, event.job?.status, event.job?.progress);

  if (
    event.job?.status === JobStatus.COMPLETED ||
    event.job?.status === JobStatus.FAILED ||
    event.job?.status === JobStatus.CANCELED
  ) {
    break;
  }
}

The SDK retries the underlying server-side stream on APIConnectionError with exponential backoff and re-emits a SNAPSHOT event on every reconnect, so resuming is idempotent on the consumer side.

List with auto-pagination

Every list method returns a Page<T> that you can either await for a single page or .autoPage() over to walk every result.

// Single page
const page = await client.jobs.list({ pagination: { limit: 50 } });
console.log(page.items, page.nextCursor);

// All items, automatically across pages
for await (const job of client.jobs.list({ pagination: { limit: 50 } }).autoPage()) {
  console.log(job.id, job.status);
}

autoPage() stops when the API returns an empty next_cursor. See Pagination for the cursor model.

Handle typed errors

Every error inherits from TranscodelyError. Match concrete classes with instanceof:

import {
  Transcodely,
  TranscodelyError,
  AuthenticationError,
  NotFoundError,
  RateLimitError,
  InvalidRequestError,
} from "@transcodely/sdk";

try {
  await client.jobs.create({ inputUrl: "...", outputs: [] });
} catch (err) {
  if (err instanceof InvalidRequestError) {
    for (const v of err.errors) {
      console.error(`${v.field}: ${v.description}`);
    }
  } else if (err instanceof RateLimitError) {
    await new Promise((r) => setTimeout(r, err.retryAfterMs ?? 1000));
  } else if (err instanceof NotFoundError) {
    console.error("not found:", err.message);
  } else if (err instanceof AuthenticationError) {
    console.error("bad API key");
  } else if (err instanceof TranscodelyError) {
    console.error(`[${err.requestId}] ${err.code}: ${err.message}`);
  } else {
    throw err;
  }
}

Every error carries code, httpStatus, requestId, and raw (the original response body). See Errors for the full hierarchy.

Override idempotency

client.jobs.create automatically generates a UUID v4 Idempotency-Key so retrying within the same process is safe. For cross-process safety (queue workers, cron jobs), pass your own:

const job = await client.jobs.create({
  inputUrl: "...",
  outputOriginId: "ori_x9y8z7w6v5",
  outputs: [/* ... */],
  idempotencyKey: `transcode_asset_${assetId}_v1`,
});

All other write methods also ship an Idempotency-Key automatically. See Idempotency for replay semantics and key-format guidance.

Request IDs

The most recent request ID is exposed for log correlation:

const job = await client.jobs.get("job_a1b2c3d4e5f6");
console.log(client.lastRequestId); // "req_..."

Errors also carry err.requestId directly.

Configuration

Pass any of these to the constructor:

OptionDefaultNotes
apiKey— required —Your Transcodely API key
baseUrlhttps://api.transcodely.comOverride for staging or self-hosted
timeoutMs30000Request timeout in milliseconds
maxRetries3Retries on network errors, 5xx, 429, 503 with jittered backoff
apiVersioncalendar version baked at SDK build timeSent as Transcodely-Version
defaultHeadersRecord<string, string> — sent on every request
fetchImplglobalThis.fetchInject a custom fetch (testing, browser shims)
logger(event: LogEvent) => void — structured request log hook
const client = new Transcodely({
  apiKey: process.env.TRANSCODELY_API_KEY!,
  timeoutMs: 60_000,
  maxRetries: 5,
  defaultHeaders: { "X-Caller": "ingest-worker" },
  logger: (event) =>
    console.log(`${event.service}/${event.method} ${event.status ?? "-"} ${event.durationMs}ms`, event.requestId),
});

LogEvent carries service, method, durationMs and attempt on every call, plus status, requestId and error when the response (or failure) supplied them. The hook fires on every attempt rather than once per call, so a retried call produces several entries — attempt distinguishes them. A logger that throws is swallowed and never breaks the request.

Where to go next

  • API Reference — the full RPC surface, with request and response shapes.
  • Webhook Integration — verify signed deliveries and handle events with client.webhooks.constructEvent.
  • Errors — the typed error hierarchy in one place.
  • Pagination — cursor model and auto-paging idioms.
  • Idempotency — when and how to set your own key.
  • SDK source on GitHub — full source, runnable examples in examples/, and changelog.