JSPM

@offload-run/sdk

0.4.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 31
  • Score
    100M100P100Q61622F
  • License MIT

Official TypeScript SDK for Offload - A simple SaaS API for long-running tasks

Package Exports

  • @offload-run/sdk

Readme

@offload-run/sdk

Official TypeScript/JavaScript SDK for Offload - A simple SaaS API for long-running tasks like video thumbnail extraction, image/video compression, and more.

Installation

npm install @offload-run/sdk
# or
pnpm add @offload-run/sdk
# or
yarn add @offload-run/sdk

Quick Start

import { OffloadClient } from "@offload-run/sdk";

// Initialize client with your API key
const client = new OffloadClient({
  apiKey: process.env.OFFLOAD_API_KEY,
});

// Enqueue a job
const { jobId } = await client.enqueueJob({
  type: "compressImage",
  params: {
    imageUrl: "https://example.com/large-image.jpg",
    quality: 80,
    maxWidth: 1920,
  },
});

// Wait for job to complete
const job = await client.waitForJob(jobId);

console.log("Compressed image:", job.result.compressedUrl);

Features

  • Type-safe - Full TypeScript support with automatic type inference based on job type
  • Promise-based - Modern async/await API
  • Webhook support - Built-in webhook verification with HMAC signatures
  • Polling helper - waitForJob() method for easy job completion waiting
  • Simple API - Single enqueueJob() method for all job types with smart type inference
  • Error handling - Clear error messages and types

Usage

Initialize Client

import { OffloadClient } from "@offload-run/sdk";

const client = new OffloadClient({
  apiKey: "your-api-key",
  baseUrl: "https://app.offload.run/api/v1", // Optional, defaults to production
  timeout: 30000, // Optional, request timeout in ms
});

Video Thumbnail Extraction

Extract a thumbnail from a video at a specific timestamp or use smart frame selection:

// Smart frame selection (auto-detect best frame)
const { jobId } = await client.enqueueJob({
  type: "videoThumbnail",
  params: {
    videoUrl: "https://example.com/video.mp4",
    timestamp: "auto", // Automatically find the most representative frame
    quality: 90, // High quality (1-100, default: 85)
    format: "jpg",
  },
});

// Traditional timestamp-based extraction
const { jobId } = await client.enqueueJob({
  type: "videoThumbnail",
  params: {
    videoUrl: "https://example.com/video.mp4",
    timestamp: 5, // 5 seconds into the video
    width: 1280,
    height: 720,
    quality: 85, // Optional quality parameter (default: 85)
    format: "jpg",
  },
});

// Wait for completion
const job = await client.waitForJob(jobId);

console.log("Thumbnail:", job.result.thumbnailUrl);
console.log("Dimensions:", job.result.width, "x", job.result.height);
console.log("Actual timestamp used:", job.result.actualTimestamp); // Only set when using numeric timestamp

Smart Frame Selection (timestamp: "auto"):

  • Automatically analyzes the first 10 seconds of the video
  • Selects the most representative frame
  • Ideal for generating meaningful preview images

Quality Parameter:

  • Range: 1-100 (default: 85)
  • Higher values = better quality but larger file size
  • Applies to JPG and WebP formats (PNG is lossless and ignores this)
  • Recommended: 75-85 for web, 90-95 for high-quality previews

Image Compression

Compress and optimize images:

const { jobId } = await client.enqueueJob({
  type: "compressImage",
  params: {
    imageUrl: "https://example.com/image.png",
    quality: 80,
    maxWidth: 1920,
    maxHeight: 1080,
    format: "webp",
    stripMetadata: true,
  },
});

const job = await client.waitForJob(jobId);

console.log("Original size:", job.result.originalSize);
console.log("Compressed size:", job.result.compressedSize);
console.log("Compression ratio:", job.result.compressionRatio + "%");
console.log("Download:", job.result.compressedUrl);

Video Compression

Compress videos with codec selection:

const { jobId } = await client.enqueueJob({
  type: "compressVideo",
  params: {
    videoUrl: "https://example.com/video.mp4",
    quality: "medium", // 'low' | 'medium' | 'high'
    maxWidth: 1920,
    codec: "h264",
    stripAudio: false,
  },
});

const job = await client.waitForJob(jobId, {
  timeout: 300000, // 5 minutes
  pollInterval: 2000, // Poll every 2 seconds
  onProgress: (job) => {
    console.log("Status:", job.status);
  },
});

console.log("Compressed video:", job.result.compressedUrl);

Extract Audio from Video

Extract audio track from a video file as MP3:

const { jobId } = await client.enqueueJob({
  type: "extractAudio",
  params: {
    url: "https://example.com/video.mp4",
    quality: "high", // 'low' (128kbps) | 'medium' (192kbps) | 'high' (320kbps)
  },
});

const job = await client.waitForJob(jobId);

console.log("Audio URL:", job.url);
console.log("Duration:", job.duration, "seconds");
console.log("Bitrate:", job.bitrate);
console.log("Format:", job.format); // Always 'mp3'
console.log("Source audio codec:", job.source.audioCodec);

Quality Presets:

  • low: 128 kbps - Smaller file size, suitable for voice recordings
  • medium: 192 kbps - Good balance of quality and size (default)
  • high: 320 kbps - Best quality, larger file size

Video Extraction (Audio + Frames)

Extract audio and/or frames from a video in a single job. This is a combined operation that can extract the audio track and capture frames at specific points:

// Extract audio + 10 evenly-spaced frames
const { jobId } = await client.enqueueJob({
  type: "videoExtraction",
  params: {
    url: "https://example.com/video.mp4",
    audio: {
      quality: "high", // 'low' (128kbps) | 'medium' (192kbps) | 'high' (320kbps)
    },
    frames: {
      mode: "count",
      count: 10, // Extract 10 evenly-spaced frames
      format: "jpg",
      quality: 85,
      skipBlackFrames: true, // Skip black/blank frames
    },
  },
});

const job = await client.waitForJob(jobId);

// Audio result (null if audio: false or video has no audio track)
if (job.result.audio) {
  console.log("Audio URL:", job.result.audio.url);
  console.log("Audio duration:", job.result.audio.duration);
  console.log("Audio format:", job.result.audio.format); // 'mp3'
}

// Frames array
for (const frame of job.result.frames) {
  console.log(`Frame at ${frame.timestamp}s:`, frame.url);
  console.log(`  Size: ${frame.width}x${frame.height}`);
}

Frame Extraction Modes:

// Count mode - extract N evenly-spaced frames
frames: { mode: "count", count: 10 }

// Percentage mode - extract frames at specific percentage points
frames: { mode: "percentage", values: [0, 25, 50, 75, 100] }

// Interval mode - extract a frame every N seconds
frames: { mode: "interval", every: 5 } // One frame every 5 seconds

// Timestamps mode - extract frames at exact timestamps (in seconds)
frames: { mode: "timestamps", values: [0, 2.5, 10, 30] }

Options:

  • audio: false — Skip audio extraction entirely
  • frames: false — Skip frame extraction entirely
  • frames.skipBlackFrames — Automatically skip black/blank frames
  • frames.width / frames.height — Resize extracted frames
  • frames.format — Output format: 'jpg' | 'png' | 'webp'
  • frames.quality — Quality 1-100 (for jpg/webp)

Enqueue and Wait (One-liner)

// Enqueue job and wait for completion in one call
const job = await client.enqueueAndWait({
  type: "compressImage",
  params: {
    imageUrl: "https://example.com/image.jpg",
    quality: 80,
  },
});

console.log("Done!", job.result.compressedUrl);

Job Management

// Get job status
const job = await client.getJob(jobId);
console.log("Status:", job.status); // 'pending' | 'processing' | 'completed' | 'failed'

// Cancel a job
await client.cancelJob(jobId);

// Wait with custom options
const job = await client.waitForJob(jobId, {
  timeout: 60000, // Max 60 seconds
  pollInterval: 1000, // Check every second
  onProgress: (job) => {
    console.log(`Job ${job.id} is ${job.status}`);
  },
});

Webhooks

Instead of polling, configure webhooks to be notified when jobs complete:

Setting up Webhooks

// When creating a job, specify webhook URL
const { jobId } = await client.enqueueJob({
  type: "compressImage",
  params: {
    imageUrl: "https://example.com/image.jpg",
    quality: 80,
  },
  webhookUrl: "https://yourapp.com/api/webhooks/offload",
});

Verifying Webhooks

// Next.js example - app/api/webhooks/offload/route.ts
import { handleWebhook } from "@offload-run/sdk";

export async function POST(req: Request) {
  try {
    const payload = await handleWebhook(req, process.env.WEBHOOK_SECRET!);

    // Process the webhook
    console.log("Job completed:", payload.jobId);
    console.log("Result:", payload.result);

    return new Response("OK", { status: 200 });
  } catch (error) {
    console.error("Webhook error:", error);
    return new Response("Unauthorized", { status: 401 });
  }
}

For Express.js, use parseWebhook() or webhookMiddleware() - see SDK exports for details.

API Reference

OffloadClient

Constructor

new OffloadClient({
  apiKey: string;
  baseUrl?: string;
  timeout?: number;
})

Methods

enqueueJob(params)

Enqueue a new job.

await client.enqueueJob({
  type: 'videoThumbnail' | 'compressImage' | 'compressVideo' | 'compressPdf' | 'extractAudio' | 'videoExtraction',
  params: { /* job-specific params */ },
  priority?: number, // 1-10, lower = higher priority
  webhookUrl?: string,
  webhookSecret?: string,
});
getJob(jobId)

Get job status and result.

cancelJob(jobId)

Cancel a pending or processing job.

waitForJob(jobId, options)

Wait for job to complete by polling.

enqueueAndWait(params, waitOptions)

Enqueue a job and wait for it to complete. Returns the completed job with result.

Error Handling

try {
  const job = await client.waitForJob(jobId);

  if (job.status === "failed") {
    console.error("Job failed:", job.error);
  } else {
    console.log("Success:", job.result);
  }
} catch (error) {
  if (error.response?.status === 401) {
    console.error("Invalid API key");
  } else if (error.response?.status === 404) {
    console.error("Job not found");
  } else {
    console.error("Error:", error.message);
  }
}

Rate Limits

Rate limits depend on your plan:

  • Free: 2 concurrent jobs, 100 jobs/hour
  • Pro: 10 concurrent jobs, 1000 jobs/hour
  • Enterprise: 50 concurrent jobs, 10000 jobs/hour

TypeScript Support

The SDK is written in TypeScript and provides full type safety:

import type {
  Job,
  CompressImageResult,
  ExtractAudioResult,
  VideoExtractionResult,
} from "@offload-run/sdk";

const imageJob: Job<"compressImage"> = await client.getJob(imageJobId);
const audioJob: Job<"extractAudio"> = await client.getJob(audioJobId);
const extractionJob: Job<"videoExtraction"> = await client.getJob(extractionJobId);

// Results are properly typed
console.log(imageJob.compressionRatio); // Type-safe access
console.log(audioJob.bitrate); // Type-safe access
console.log(extractionJob.frames.length); // Type-safe access

License

MIT

Support