DEV Community

Cover image for Shipping Async Video Background Removal at $0.10/sec
Troy Lorents
Troy Lorents

Posted on

Shipping Async Video Background Removal at $0.10/sec

Why async matters for video

I've been running useKnockout - a background removal API that processes images in ~200ms - for a few months. Images are fast enough to handle synchronously: POST a file, wait 200ms, get a PNG back.

Video is different. Even a 5-second clip at 30fps is 150 frames. At 200ms per frame, that's 30 seconds of processing. You can't hold an HTTP connection open for 30 seconds and call it a good API.

So today I shipped POST /video/remove - async video background removal that returns a job ID immediately, processes in the background, and gives you ProRes 4444 (RGB+alpha) when it's done.

What shipped

As of v0.11.0 (July 10, 2026):

  • POST /video/remove - upload a video, get a job ID back
  • GET /jobs/{job_id} - poll for status, download the result when ready
  • ProRes 4444 output - RGB with full alpha channel, ready to drop into Premiere/Final Cut/DaVinci
  • Node SDK videoRemove() and getJob() in v0.7.0
  • Python SDK video_remove() and get_job() in v0.7.0

Billing is a dedicated video.seconds meter at $0.10/sec (different from the per-image rate), with a 15-second cap to keep costs predictable.

How to use it (Node SDK)

import { useKnockout } from 'useknockout-node';
import fs from 'fs';

const client = useKnockout({ apiKey: process.env.KNOCKOUT_API_KEY });

// Submit the video
const job = await client.videoRemove({
  file: fs.createReadStream('./input.mp4')
});

console.log('Job ID:', job.id);

// Poll until done
let status = await client.getJob(job.id);
while (status.status === 'processing') {
  await new Promise(resolve => setTimeout(resolve, 2000));
  status = await client.getJob(job.id);
}

if (status.status === 'completed') {
  // Download the ProRes 4444 result
  const video = await fetch(status.result_url);
  const buffer = await video.arrayBuffer();
  fs.writeFileSync('./output.mov', Buffer.from(buffer));
}
Enter fullscreen mode Exit fullscreen mode

The job object includes duration_seconds (billed amount), status (processing/completed/failed), and result_url when done.

Billing: $0.10/sec, 15s cap

Video uses its own meter (video.seconds) so you can track image vs. video usage separately. A 7-second clip costs $0.70. Clips longer than 15 seconds are trimmed (the 15s cap keeps runaway bills from killing indie projects).

For context, useKnockout's image endpoint is ~$0.005/image on the starter tier - about 40x cheaper than remove.bg's $0.11-$0.23/image. Video is more expensive per second of processing, but still priced for solo builders.

Other recent additions

Before video, I shipped:

  • POST /collage (July 3) - N-photo product collages, billed N units. Pass 4 product shots, get a 2×2 grid back.
  • RealESRGAN upscaling (July 2) - default upscale switched to RealESRGAN with refined alpha matting and linear compositing.
  • studio-shot enhance (June 22) - GFPGAN face restoration now available on /studio-shot with enhance=true.

All of these shipped in the last month because the API is self-hostable (MIT-licensed) and I'm the only one shipping it - no committee, no quarters, just daily commits.

Why this matters

Most indie devs I talk to are either:

  1. Paying remove.bg [placeholder: $X/mo] and wincing at the bill, or
  2. Avoiding background removal features entirely because the APIs are too expensive.

Video makes it worse - remove.bg doesn't do video at all. Other video APIs charge per frame (30fps × 5sec = 150 frames × $0.10 = $15 for 5 seconds).

At $0.10/sec, that same 5-second clip costs $0.50. Still not free, but it's in "I can actually ship this feature" territory.

What's next

I'm working on batch video (submit 10 clips, get 10 job IDs back) and a webhook callback option so you don't have to poll. The async job pattern is solid now - those are just API surface.

If you're building something that needs video background removal (marketplace demos, avatar videos, TikTok filters, whatever), the SDKs are on npm/PyPI and the API is at useknockout.com. Free tier includes 10 images/month across 5 endpoints. Video is pay-as-you-go.

No lock-in - it's MIT-licensed if you want to self-host. But the hosted version is live and you can start with a curl:

curl -X POST https://useknockout--api.modal.run/video/remove \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "file=@input.mp4"
Enter fullscreen mode Exit fullscreen mode

You'll get a job ID back. Poll /jobs/{id} until it's done, then download the ProRes file. That's it.

Top comments (0)