SDK Reference
Complete reference for the @offload-run/sdk TypeScript/JavaScript SDK.
Installation
npm install @offload-run/sdk # or pnpm add @offload-run/sdk # or yarn add @offload-run/sdk
Getting Your API Key
To use the SDK, you need an API key from your offload.run dashboard:
- Sign in to your account at app.offload.run
- Navigate to Settings → API Keys
- Click Create New Key and give it a name
- Copy the key and store it securely (it won't be shown again)
Security tip: Never commit your API key to version control. Use environment variables like OFFLOAD_API_KEY instead.
OffloadClient
The main client for interacting with the offload.run API.
import { OffloadClient } from '@offload-run/sdk';
const client = new OffloadClient({
apiKey: process.env.OFFLOAD_API_KEY!, // Required
baseUrl: 'https://app.offload.run/api/v1', // Optional (default)
timeout: 30000, // Optional: request timeout in ms (default: 30000)
});Methods
enqueueJob()
Enqueue a new job for processing. Returns immediately with a job ID.
const { jobId, status, createdAt } = await client.enqueueJob({
type: 'compressVideo', // Required: Job type
params: { // Required: Type-specific parameters
url: 'https://example.com/video.mp4',
quality: 'high',
},
priority: 5, // Optional: 1-10, lower = higher priority
webhookUrl: 'https://...', // Optional: URL for completion notification
webhookSecret: 'secret', // Optional: Secret for webhook signature
});getJob()
Get the current status and result of a job.
const job = await client.getJob(jobId); console.log(job.id); // Job ID console.log(job.type); // 'compressVideo', 'compressImage', etc. console.log(job.status); // 'pending' | 'processing' | 'completed' | 'failed' | 'cancelled' console.log(job.error); // Error message if failed, null otherwise // Result fields are at root level when completed: console.log(job.url); // Output file URL console.log(job.size); // Output file size console.log(job.compressionRatio); // e.g., 0.65 (65% of original)
cancelJob()
Cancel a pending or processing job. Cannot cancel completed jobs.
const { success, jobId } = await client.cancelJob(jobId);
if (success) {
console.log('Job cancelled');
}waitForJob()
Poll for job completion. Useful when you need the result immediately.
const job = await client.waitForJob(jobId, {
timeout: 60000, // Max wait time in ms (default: 60000)
pollInterval: 500, // Poll interval in ms (default: 500)
onProgress: (job) => {
console.log(`Status: ${job.status}`);
},
});
if (job.status === 'completed') {
console.log('Output URL:', job.url);
} else if (job.status === 'failed') {
console.error('Job failed:', job.error);
}enqueueAndWait()
Convenience method that enqueues a job and waits for completion in one call.
// Perfect for simple use cases
const job = await client.enqueueAndWait({
type: 'compressImage',
params: {
url: 'https://example.com/image.jpg',
quality: 80,
}
}, {
timeout: 120000, // 2 minutes max
});
console.log('Compressed URL:', job.url);
console.log('Original size:', job.source.size);
console.log('Compressed size:', job.size);Job Types
The SDK supports 5 job types. Each has specific parameters and returns different result fields.
videoThumbnail
VideoExtract a thumbnail from a video. Use timestamp: 'auto' to automatically find the best frame.
Parameters:
{
url: string; // Video URL (required)
timestamp?: number | 'auto'; // Frame time in seconds, or 'auto' for smart selection
width?: number; // Output width in pixels
height?: number; // Output height in pixels
format?: 'jpg' | 'png' | 'webp'; // Output format (default: 'jpg')
quality?: number; // Quality 1-100 (default: 85)
}Result:
{
url: string; // Thumbnail URL
size: number; // File size in bytes
width: number; // Actual width
height: number; // Actual height
format: string; // Output format
timestamp?: number; // Actual timestamp used
source: { // Source video info
size: number;
width: number;
height: number;
duration: number;
}
}Example:
const job = await client.enqueueAndWait({
type: 'videoThumbnail',
params: {
url: 'https://example.com/video.mp4',
timestamp: 'auto', // Smart frame selection
format: 'webp',
quality: 90,
}
});
console.log('Thumbnail:', job.url);compressImage
ImageCompress and optimize images with format conversion and resizing.
Parameters:
{
url: string; // Image URL (required)
quality?: number; // Quality 1-100 (default: 80)
maxWidth?: number; // Max width (maintains aspect ratio)
maxHeight?: number; // Max height (maintains aspect ratio)
format?: 'jpg' | 'png' | 'webp' | 'avif'; // Output format
stripMetadata?: boolean; // Remove EXIF data (default: false)
}Result:
{
url: string; // Compressed image URL
size: number; // Output size in bytes
width: number; // Output width
height: number; // Output height
format: string; // Output format
compressionRatio: number; // e.g., 0.45 means 45% of original
source: { // Source image info
size: number;
width: number;
height: number;
}
}Example:
const job = await client.enqueueAndWait({
type: 'compressImage',
params: {
url: 'https://example.com/photo.png',
quality: 80,
maxWidth: 1920,
format: 'webp',
stripMetadata: true,
}
});
console.log('Saved', (1 - job.compressionRatio) * 100, '% in file size');compressVideo
VideoCompress videos for web playback with codec selection and quality presets.
Parameters:
{
url: string; // Video URL (required)
quality?: 'low' | 'medium' | 'high'; // Quality preset (default: 'medium')
maxWidth?: number; // Max width in pixels
maxHeight?: number; // Max height in pixels
targetBitrate?: string; // e.g., '1M', '2M', '500K'
codec?: 'h264' | 'h265' | 'vp9'; // Video codec (default: 'h264')
stripAudio?: boolean; // Remove audio track (default: false)
}Result:
{
url: string; // Compressed video URL
size: number; // Output size in bytes
width: number; // Output width
height: number; // Output height
duration: number; // Duration in seconds
format: string; // Container format
codec: string; // Video codec used
bitrate: string; // Actual bitrate
compressionRatio: number; // Ratio vs original
source: { // Source video info
size: number;
width: number;
height: number;
duration: number;
}
}Example:
const job = await client.enqueueAndWait({
type: 'compressVideo',
params: {
url: 'https://example.com/video.mp4',
quality: 'high',
maxWidth: 1920,
codec: 'h264',
}
}, {
timeout: 300000, // 5 minutes for video
onProgress: (j) => console.log(j.status),
});compressPdf
PDFCompress PDF documents while maintaining readability.
Parameters:
{
url: string; // PDF URL (required)
quality?: 'low' | 'medium' | 'high'; // Quality preset (default: 'medium')
removeMetadata?: boolean; // Remove document metadata (default: false)
}Result:
{
url: string; // Compressed PDF URL
size: number; // Output size in bytes
format: string; // Always 'pdf'
compressionRatio: number; // Ratio vs original
source: {
size: number;
}
}Example:
const job = await client.enqueueAndWait({
type: 'compressPdf',
params: {
url: 'https://example.com/document.pdf',
quality: 'medium',
removeMetadata: true,
}
});extractAudio
AudioExtract audio track from video files as MP3.
Parameters:
{
url: string; // Video URL (required)
quality?: 'low' | 'medium' | 'high'; // Audio quality (default: 'medium')
// low: 128kbps, medium: 192kbps, high: 320kbps
}Result:
{
url: string; // MP3 file URL
size: number; // File size in bytes
duration: number; // Duration in seconds
format: 'mp3'; // Always 'mp3'
bitrate: string; // e.g., '320kbps'
source: {
size: number;
duration: number;
audioBitrate: number;
audioCodec: string;
}
}Example:
const job = await client.enqueueAndWait({
type: 'extractAudio',
params: {
url: 'https://example.com/video.mp4',
quality: 'high', // 320kbps
}
});
console.log('Audio URL:', job.url);
console.log('Duration:', job.duration, 'seconds');Webhooks
Instead of polling with waitForJob(), you can receive notifications via webhooks when jobs complete.
Setting up webhooks:
await client.enqueueJob({
type: 'compressImage',
params: { url: '...' },
webhookUrl: 'https://yourapp.com/api/webhooks/offload',
webhookSecret: process.env.WEBHOOK_SECRET, // For signature verification
});Webhook headers:
x-offload-signature: sha256=abc123... // HMAC-SHA256 signature x-offload-timestamp: 1703123456789 // Unix timestamp in ms
Webhook payload (same structure as job result):
{
"id": "job_abc123",
"type": "compressImage",
"status": "completed", // or "failed"
"error": null, // Error message if failed
"url": "https://...", // Result fields at root level
"size": 102400,
"compressionRatio": 0.45,
// ... other result fields
}Handling webhooks with SDK helpers:
// Next.js App Router
import { handleWebhook } from '@offload-run/sdk';
export async function POST(request: Request) {
const payload = await handleWebhook(request, process.env.WEBHOOK_SECRET!);
// payload is verified and typed
console.log(payload.id, payload.status, payload.url);
return new Response('OK');
}Rate Limits
Rate limits depend on your subscription plan:
TypeScript Support
The SDK provides full TypeScript support with automatic type inference based on job type:
import type {
JobType,
JobStatus,
Job,
// Parameters
VideoThumbnailParams,
CompressImageParams,
CompressVideoParams,
CompressPdfParams,
ExtractAudioParams,
// Results
VideoThumbnailResult,
CompressImageResult,
CompressVideoResult,
CompressPdfResult,
ExtractAudioResult,
// Webhooks
WebhookPayload,
} from '@offload-run/sdk';
// Type-safe job access
const imageJob: Job<'compressImage'> = await client.getJob(id);
console.log(imageJob.compressionRatio); // TypeScript knows this existsError Handling
try {
const job = await client.enqueueAndWait({
type: 'compressVideo',
params: { url: '...' }
});
if (job.status === 'failed') {
console.error('Job failed:', job.error);
return;
}
console.log('Success:', job.url);
} catch (error) {
if (error.response?.status === 401) {
console.error('Invalid API key');
} else if (error.response?.status === 429) {
console.error('Rate limit exceeded');
} else if (error.message.includes('Timeout')) {
console.error('Job took too long');
} else {
console.error('Error:', error.message);
}
}