Official TypeScript/JavaScript SDK for the Poof background removal API: images and video.
Migrating from remove.bg? It shuts down on 1 December 2026 — see the remove.bg alternative and migration guide.
npm install @poof-bg/js
# or
yarn add @poof-bg/js
# or
pnpm add @poof-bg/jsimport { Poof } from '@poof-bg/js';
const poof = new Poof({ apiKey: 'your-api-key' });
// Remove background from an image
const result = await poof.removeBackground(imageFile);
// result.data is an ArrayBuffer containing the processed image
// result.metadata contains processing info
// Remove background from a video (returns a job; wait for the result)
const done = await poof.removeBackground(videoFile, { wait: true });
// done.resultUrl is a direct download link to the transparent WebMimport { Poof } from '@poof-bg/js';
const poof = new Poof({ apiKey: process.env.POOF_API_KEY! });
// From File (browser)
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];
const result = await poof.removeBackground(file);
// Create a Blob for display
const blob = new Blob([result.data], { type: result.metadata.contentType });
const url = URL.createObjectURL(blob);const result = await poof.removeBackground(file, {
format: 'webp', // Output format: 'png' | 'jpg' | 'webp'
crop: true, // Crop to subject bounds, or an aspect ratio like '1:1'
padding: '10%', // Padding around the subject when cropping
size: 'medium', // Size preset: 'full' | 'preview' | 'medium' | 'hd'
channels: 'rgb', // 'rgba' for transparency, 'rgb' for opaque, 'alpha' for the mask
bgColor: '#ffffff', // Fills the background, so the result is opaque (omit for transparency; ignored with 'alpha')
});Resize to an exact size without stretching. fit decides what happens with the aspect-ratio difference: contain (default) pads, cover crops the overflow around the subject, scale-down pads without ever enlarging a smaller image. Setting only width or height keeps the aspect ratio. size is ignored when a fixed size is set.
// 500x500 product image: subject cropped, padded and centred
const result = await poof.removeBackground(file, { crop: true, width: 500, height: 500 });
// Fill a 1080x1080 canvas on white instead
const result = await poof.removeBackground(file, {
width: 1080,
height: 1080,
fit: 'cover',
format: 'jpg',
bgColor: '#ffffff',
});
// Cap the width at 1000px, keep the aspect ratio, leave smaller images alone
const result = await poof.removeBackground(file, { width: 1000, fit: 'scale-down' });import { Poof } from '@poof-bg/js';
import fs from 'node:fs';
const poof = new Poof({ apiKey: process.env.POOF_API_KEY! });
// From file path (Node.js only)
const result = await poof.removeBackground('./input.jpg');
// Save to file
fs.writeFileSync('output.png', Buffer.from(result.data));import { Poof } from '@poof-bg/js';
import fs from 'node:fs';
const poof = new Poof({ apiKey: process.env.POOF_API_KEY! });
const imageBuffer = fs.readFileSync('./input.jpg');
const result = await poof.removeBackground(imageBuffer);
fs.writeFileSync('output.png', Buffer.from(result.data));Videos go through the same removeBackground() call. Because a clip takes a while to process, the call returns a Job instead of bytes: wait for it with waitForJob() (or pass wait: true), then download resultUrl. Output is a transparent VP9 WebM by default; format can also be mov (ProRes 4444 with alpha), mp4 (H.264, composited on bgColor, default white) or gif (looping GIF with 1-bit transparency and no audio, capped at 1280 px on the longest side (720p for widescreen) and 50 fps, for stickers and chat; pass size: 'preview' for sticker-sized output). Audio is kept, except for gif output and channels: 'alpha' (matte) output, which have no audio track. An animated GIF is accepted as input too and is processed as a video; a still GIF is handled like any other image. crop, padding, width, height and fit are image-only options.
Pricing: 5 credits per second of video, rounded up to the next whole second (10 credits per second above 30 fps). Animated GIF input is always 5 credits per second, whatever its frame rate. Credits are charged when the job is accepted and refunded automatically if it fails. If your remaining credits don't cover the whole clip, the upload is rejected with a 402 payment_required (PaymentRequiredError) and nothing is charged.
import { Poof } from '@poof-bg/js';
import fs from 'node:fs';
const poof = new Poof({ apiKey: process.env.POOF_API_KEY! });
// Submit the video: file path (Node.js), File, Blob, Buffer, or { url }
const job = await poof.removeBackground('./clip.mp4', { format: 'webm' });
console.log(`Job ${job.id}: ${job.durationSeconds}s, ${job.credits} credits`);
// Wait for it to finish (polls every 3 s by default; polling is free)
const done = await poof.waitForJob(job, {
onProgress: (j) => console.log(`${j.status} ${Math.round(j.progress * 100)}%`),
});
// Download the result (no API key needed; the link is valid for 24 hours)
const response = await fetch(done.resultUrl!);
fs.writeFileSync('clip-nobg.webm', Buffer.from(await response.arrayBuffer()));Shorter: wait: true submits and waits in one call.
const done = await poof.removeBackground('./clip.mp4', {
format: 'mp4', // 'webm' (transparent) | 'mov' (transparent ProRes) | 'mp4' (opaque) | 'gif' (1-bit transparent)
channels: 'rgba', // 'rgba' | 'rgb' | 'alpha' (grayscale matte video, no audio)
bgColor: '#00ff00', // Fills the background, so the video is opaque in any format; mp4/'rgb' default to white
size: 'hd', // Longest side cap: 640 / 1280 / 1920 (hd); 'full' keeps the source up to 4K
wait: { pollIntervalMs: 5000, timeoutMs: 15 * 60 * 1000 },
});Large videos can be submitted by URL instead of uploading (up to 500 MB; uploads are capped at 100 MB). { url } works for images too.
const job = await poof.removeBackground({ url: 'https://example.com/clip.mp4' });Check on a job later with getJob():
const job = await poof.getJob('job_8f3a1c2e9b7d4a10');
if (job.status === 'completed') console.log(job.resultUrl);When the input could be either an image or a video, use isJob() to tell the results apart:
import { Poof, isJob } from '@poof-bg/js';
const result = await poof.removeBackground(upload);
if (isJob(result)) {
const done = await poof.waitForJob(result);
console.log('Video ready:', done.resultUrl);
} else {
fs.writeFileSync('output.png', Buffer.from(result.data));
}A failed job makes waitForJob() throw a PoofError with code job_failed; the credits are refunded automatically.
Limits: up to 5 minutes of video, 100 MB per upload, 500 MB when submitted by { url }.
const account = await poof.me();
console.log(`Organization: ${account.organizationId}`);
console.log(`Plan: ${account.plan}`);
console.log(`Credits: ${account.usedCredits} / ${account.maxCredits}`);const result = await poof.removeBackground(file);
console.log(`Request ID: ${result.metadata.requestId}`);
console.log(`Processing time: ${result.metadata.processingTimeMs}ms`);
console.log(`Output size: ${result.metadata.width}x${result.metadata.height}`);
console.log(`Content type: ${result.metadata.contentType}`);The SDK provides typed error classes for different error scenarios:
import {
Poof,
PoofError,
AuthenticationError,
PaymentRequiredError,
RateLimitError,
ValidationError,
} from '@poof-bg/js';
try {
const result = await poof.removeBackground(file);
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('Invalid API key');
} else if (error instanceof PaymentRequiredError) {
console.error('Insufficient credits (for video: not enough left for the whole clip)');
} else if (error instanceof RateLimitError) {
console.error('Rate limit exceeded, retry later');
} else if (error instanceof ValidationError) {
console.error('Invalid input:', error.details); // e.g. { field: 'crop' }
console.error('Docs:', error.docUrl);
} else if (error instanceof PoofError && error.code === 'job_failed') {
console.error('Video job failed, credits refunded:', error.message);
} else if (error instanceof PoofError) {
console.error(`API error: ${error.message} (${error.code})`);
console.error(`Request ID: ${error.requestId}`);
} else {
throw error;
}
}const poof = new Poof({
apiKey: 'your-api-key',
baseUrl: 'https://api.poof.bg/v1', // Optional: custom base URL
timeout: 60000, // Optional: request timeout in ms
});| Input Type | Browser | Node.js |
|---|---|---|
File |
✅ | ✅ |
Blob |
✅ | ✅ |
ArrayBuffer |
✅ | ✅ |
Uint8Array |
✅ | ✅ |
Buffer |
❌ | ✅ |
| File path (string) | ❌ | ✅ |
{ url: string } (public https URL) |
✅ | ✅ |
Images: PNG, JPEG, WebP or a still (single-frame) GIF, up to 20 MB. Videos: MP4, MOV, WebM, M4V, MKV, animated GIF (two or more frames), up to 100 MB uploaded or 500 MB by URL, up to 5 minutes.
Full TypeScript support with exported types:
import type {
PoofOptions,
MediaInput,
RemoveBackgroundOptions,
RemoveBackgroundResult,
ProcessingMetadata,
Job,
JobStatus,
WaitForJobOptions,
AccountInfo,
ImageFormat,
VideoFormat,
ImageSize,
Channels,
} from '@poof-bg/js';- Node.js 18+ (uses native
fetch) - Modern browsers with
fetchandFormDatasupport
MIT