npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@rendobar/sdk

v5.15.0

Published

Rendobar SDK: TypeScript client for the media processing and AI generation API.

Readme

@rendobar/sdk is the official TypeScript client for Rendobar, a serverless media processing API. You submit a job, the SDK polls it to completion, and you get back a hosted URL. It ships ESM and CJS builds with full types, retries 429 and 5xx automatically, auto-paginates, and accepts an AbortSignal on every method.

Let your AI assistant do it

Paste this one line into Claude, ChatGPT, or Cursor and the assistant reads the full integration playbook, detects your stack, and wires Rendobar into your codebase. Your API key stays in your env file, never in the chat.

Add Rendobar to my app: read and follow https://rendobar.com/prompts/integrate.md

Requirements

Node 18 or later. Works in any runtime with fetch, including Cloudflare Workers, Deno, and Bun.

Install

npm install @rendobar/sdk

Quickstart

Run an FFmpeg command against a remote file and wait for the result. Put input URLs directly in -i positions; the SDK stages them before execution.

import { createClient, outputUrl } from "@rendobar/sdk";

const client = createClient({ apiKey: process.env.RENDOBAR_API_KEY });

const job = await client.jobs.create({
  type: "ffmpeg",
  params: {
    command:
      "ffmpeg -i https://cdn.example.com/source.mp4 " +
      "-vf scale=1280:720 -c:v libx264 -crf 23 output.mp4",
  },
});

const done = await client.jobs.wait(job.id);
console.log(outputUrl(done));

Prefer named inputs? Pass them in inputs and reference the keys in the command:

await client.jobs.create({
  type: "ffmpeg",
  inputs: { source: "https://cdn.example.com/source.mp4" },
  params: { command: "ffmpeg -i source -vf scale=1280:720 output.mp4" },
});

ffmpeg is one of several job types. Composition, compression to a size budget, subtitle burn-in, animated captions, and media inspection each have their own. Browse them at rendobar.com/docs/jobs, or call client.jobs.types() to read the live registry.

Reading a result

Every job type returns the same output shape, so you write this handling once.

type Output = {
  data: unknown;            // structured result (probe, detections, transcript), or null
  file: OutputFile | null;  // the headline file or stream manifest, or null
  files: OutputFile[];      // every produced file (empty for data-only jobs)
  expiresAt: number | null; // Unix ms URL expiry, set when files is non-empty
};

type OutputFile = {
  url: string;   // ready-to-fetch, time-limited URL
  path: string;  // path within the job output
  type: "video" | "image" | "audio" | "captions" | "playlist" | "data" | "other";
  size: number;  // bytes
  sha256: string | null; // SHA-256 of the file as lowercase hex, null when not computed
  meta?: { format?: string; width?: number; height?: number; durationMs?: number };
};

output exists only when status === "complete", so narrow on status before reading it.

import { outputUrl, jobData } from "@rendobar/sdk";

const job = await client.jobs.wait("job_abc");

// Headline URL: the single file, or a stream manifest (.m3u8/.mpd). Undefined
// for data-only jobs and for pure file sets with no single headline.
const url = outputUrl(job);

if (job.status === "complete") {
  for (const f of job.output.files) {
    console.log(f.type, f.path, f.size, f.url);
  }
}

if (job.status === "failed") {
  console.log(job.error.code, job.error.message, job.error.detail);
}

Inspection jobs put their answer in output.data, which is unknown at the contract level because its shape depends on the job type. jobData<T>(job) reads it as your expected type:

type Probe = { format: string; durationMs: number; width: number; height: number };

const probe = jobData<Probe>(await client.jobs.wait("job_abc")); // Probe | null

T is your claim about the shape, not a guarantee. If the input is untrusted, validate output.data with Zod rather than asserting.

Errors

import { isApiError } from "@rendobar/sdk";

try {
  await client.jobs.create({ type: "ffmpeg", params: { command } });
} catch (err) {
  if (isApiError(err)) {
    console.log(err.code);       // "INSUFFICIENT_CREDITS"
    console.log(err.statusCode); // 402
    console.log(err.retryAfter); // seconds, on 429s
  }
}

Codes: UNAUTHORIZED, FORBIDDEN, VALIDATION_ERROR, INSUFFICIENT_CREDITS, RATE_LIMITED, NOT_FOUND, CONFLICT, INTERNAL_ERROR. Full reference at rendobar.com/docs/support/errors.

429 and 5xx retry automatically with exponential backoff (500ms, then 1s), twice by default, respecting Retry-After. 400, 401, 403 and 404 throw immediately.

The rest of the API

await client.jobs.create({ type, inputs, params, idempotencyKey });
await client.jobs.get("job_abc");
await client.jobs.cancel("job_abc");
await client.jobs.logs("job_abc");
await client.jobs.getTimings("job_abc");
await client.jobs.types();                    // live job registry

const page = await client.jobs.list({ status: "complete", limit: 20 });
// page.data: Job[], page.meta: { total, page, limit }

for await (const job of client.jobs.listAll({ status: "complete" })) { /* ... */ }

await client.jobs.wait("job_abc", {
  timeout: 300_000,
  interval: 2_000,
  onProgress: (job) => console.log(job.status),
  signal: controller.signal,
});

const response = await client.jobs.download("job_abc"); // raw Response

ComposeParams types the whole timeline, so an unknown field or a misspelt effect is a compile error rather than a 400.

import { createClient, type ComposeParams } from "@rendobar/sdk";

const params: ComposeParams = {
  schemaVersion: 1,
  output: { format: "mp4", size: "1080p" },
  timeline: {
    tracks: [
      {
        clips: [
          {
            asset: { type: "video", src: "https://example.com/clip.mp4" },
            effects: [{ type: "color", saturation: 1.2 }],
            transform: {
              scale: [
                { time: "0%", value: 1, easing: "easeInOutSine" },
                { time: "100%", value: 1.12 },
              ],
            },
          },
        ],
      },
    ],
  },
};

const job = await client.jobs.create({ type: "compose", params });

validate parses a timeline exactly as a render would, for free, and returns the params as the engine reads them, any warnings, and the price: what a minute of output costs and the fewest seconds a render bills. A video above your plan comes back invalid, with upgradeTo naming the plan that renders it. effects returns the reference for every effect, which is what an agent reads before it writes one.

const check = await client.compose.validate(params);
if (check.valid) console.log(check.price); // { perMinuteUsd: 0.04, minSeconds: 15 }
else console.error(check.problems);

const reference = await client.compose.effects();
const { downloadUrl } = await client.uploads.upload(file, { filename: "input.mp4" });

await client.batches.create({
  jobs: [
    { type: "ffmpeg", inputs: { source: url1 }, params: { command } },
    { type: "ffmpeg", inputs: { source: url2 }, params: { command } },
  ],
});

await client.billing.state();
await client.billing.usage({ start: "2026-01-01", end: "2026-03-29" });
await client.billing.transactions({ page: 1, limit: 50 });

// Start or change a paid plan: "pro" or "studio".
const plan = await client.billing.checkoutPlan("studio");
if (plan.kind === "checkout") console.log(plan.checkoutUrl);
await client.webhooks.create({
  name: "My Webhook",
  url: "https://example.com/webhook",
  subscribedEvents: ["job.completed", "job.failed"],
});

Verify a delivery with the raw body, not parsed JSON. verifyWebhook reads the signature and timestamp headers, rebuilds the signed string, checks the HMAC, rejects stale deliveries, and handles secret rotation.

import { verifyWebhook } from "@rendobar/sdk/webhooks";

if (!(await verifyWebhook(rawBody, request.headers, signingSecret))) {
  throw new Error("invalid signature");
}

See the webhooks guide.

Realtime needs session cookie auth. On an API key, use jobs.wait() instead.

const connection = client.realtime.connect({
  onEvent: (e) => console.log(e.type, e),
  onLive: () => console.log("replay complete, now live"),
  onResync: () => console.log("buffer overflow, refresh data"),
});

const sub = client.realtime.subscribeJob("job_abc", {
  onProgress: (e) => console.log(`${e.progress}%`),
  onComplete: (e) => console.log("done", e.status),
});

connection.disconnect();
sub.unsubscribe();
const client = createClient({
  apiKey: "rb_...",
  baseUrl: "https://api.rendobar.com", // default
  timeout: 30_000,                     // default 30s
  maxRetries: 2,                       // default 2
  orgId: "org_abc",                    // X-Org-Id header
  debug: false,                        // log request metadata
  fetch: customFetch,                  // inject a fetch, for tests
});

// Browser session cookie instead of a key
createClient({ baseUrl: "https://api.rendobar.com", credentials: "include" });

Response types are all exported: Job, JobStep, Output, OutputFile, FileType, JobError, Cost, JobType, BillingState, Transaction, UsageSummary, WebhookEndpoint, WebhookDelivery, Organization, ApiKey, and PaginationMeta.