curl https://api.poof.bg/v1/jobs/job_8f3a1c2e9b7d4a10 \
-H "x-api-key: YOUR_API_KEY"
{
"id": "job_8f3a1c2e9b7d4a10",
"status": "processing",
"progress": 0.42,
"created_at": "2026-10-12T10:12:00Z",
"started_at": "2026-10-12T10:12:04Z",
"completed_at": null,
"duration_seconds": 12.4,
"fps": 29.97,
"width": 1920,
"height": 1080,
"frames": 372,
"credits": 65,
"format": "webm",
"channels": "rgba",
"status_url": "https://api.poof.bg/v1/jobs/job_8f3a1c2e9b7d4a10",
"result_url": null,
"expires_at": null,
"error": null,
"stats": null
}
{
"id": "job_8f3a1c2e9b7d4a10",
"status": "completed",
"progress": 1,
"created_at": "2026-10-12T10:12:00Z",
"started_at": "2026-10-12T10:12:04Z",
"completed_at": "2026-10-12T10:12:41Z",
"duration_seconds": 12.4,
"fps": 29.97,
"width": 1920,
"height": 1080,
"frames": 372,
"credits": 65,
"format": "webm",
"channels": "rgba",
"status_url": "https://api.poof.bg/v1/jobs/job_8f3a1c2e9b7d4a10",
"result_url": "https://cdn.poof.bg/1d/jobs/job_8f3a1c2e9b7d4a10/result.webm",
"expires_at": "2026-10-13T10:12:41Z",
"error": null,
"stats": {
"output_width": 1920,
"output_height": 1080,
"processing_seconds": 37.2,
"matting_seconds": 28.9,
"decode_wait_seconds": 1.4,
"encode_wait_seconds": 3.1,
"scene_cuts": 2,
"output_bytes": 18734211
}
}
{
"id": "job_8f3a1c2e9b7d4a10",
"status": "failed",
"progress": 0.42,
"created_at": "2026-10-12T10:12:00Z",
"started_at": "2026-10-12T10:12:04Z",
"completed_at": "2026-10-12T10:12:20Z",
"duration_seconds": 12.4,
"fps": 29.97,
"width": 1920,
"height": 1080,
"frames": 372,
"credits": 65,
"format": "webm",
"channels": "rgba",
"status_url": "https://api.poof.bg/v1/jobs/job_8f3a1c2e9b7d4a10",
"result_url": null,
"expires_at": null,
"error": {
"code": "processing_failed",
"message": "The model or encoder failed on this video."
},
"stats": null
}
API Reference
Get Job
Check the status and progress of a background job
GET
/
jobs
/
{id}
curl https://api.poof.bg/v1/jobs/job_8f3a1c2e9b7d4a10 \
-H "x-api-key: YOUR_API_KEY"
{
"id": "job_8f3a1c2e9b7d4a10",
"status": "processing",
"progress": 0.42,
"created_at": "2026-10-12T10:12:00Z",
"started_at": "2026-10-12T10:12:04Z",
"completed_at": null,
"duration_seconds": 12.4,
"fps": 29.97,
"width": 1920,
"height": 1080,
"frames": 372,
"credits": 65,
"format": "webm",
"channels": "rgba",
"status_url": "https://api.poof.bg/v1/jobs/job_8f3a1c2e9b7d4a10",
"result_url": null,
"expires_at": null,
"error": null,
"stats": null
}
{
"id": "job_8f3a1c2e9b7d4a10",
"status": "completed",
"progress": 1,
"created_at": "2026-10-12T10:12:00Z",
"started_at": "2026-10-12T10:12:04Z",
"completed_at": "2026-10-12T10:12:41Z",
"duration_seconds": 12.4,
"fps": 29.97,
"width": 1920,
"height": 1080,
"frames": 372,
"credits": 65,
"format": "webm",
"channels": "rgba",
"status_url": "https://api.poof.bg/v1/jobs/job_8f3a1c2e9b7d4a10",
"result_url": "https://cdn.poof.bg/1d/jobs/job_8f3a1c2e9b7d4a10/result.webm",
"expires_at": "2026-10-13T10:12:41Z",
"error": null,
"stats": {
"output_width": 1920,
"output_height": 1080,
"processing_seconds": 37.2,
"matting_seconds": 28.9,
"decode_wait_seconds": 1.4,
"encode_wait_seconds": 3.1,
"scene_cuts": 2,
"output_bytes": 18734211
}
}
{
"id": "job_8f3a1c2e9b7d4a10",
"status": "failed",
"progress": 0.42,
"created_at": "2026-10-12T10:12:00Z",
"started_at": "2026-10-12T10:12:04Z",
"completed_at": "2026-10-12T10:12:20Z",
"duration_seconds": 12.4,
"fps": 29.97,
"width": 1920,
"height": 1080,
"frames": 372,
"credits": 65,
"format": "webm",
"channels": "rgba",
"status_url": "https://api.poof.bg/v1/jobs/job_8f3a1c2e9b7d4a10",
"result_url": null,
"expires_at": null,
"error": {
"code": "processing_failed",
"message": "The model or encoder failed on this video."
},
"stats": null
}
Returns the current state of a job created by
Every response carries an
See API Error Codes for the error body format.
POST /remove for a video, including its progress and, once it has completed, the result_url to download the output from.
This endpoint is free: polling does not consume credits, and GET requests under /jobs/ are exempt from your plan’s per-organisation rate limit.
Request
string
required
The job ID returned as
id in the 202 response to POST /remove (starts with job_).curl https://api.poof.bg/v1/jobs/job_8f3a1c2e9b7d4a10 \
-H "x-api-key: pk_your_api_key"
Response
string
Job ID, prefixed with
job_.string
One of
queued, processing, completed or failed. See Status lifecycle.number
Fraction of the video processed so far, from
0 to 1. It is 0 while queued and 1 once completed.string
ISO 8601 timestamp of when the job was accepted.
string | null
When processing began.
null while the job is still queued.string | null
When the job reached
completed or failed. null until then.number
Source video duration in seconds.
number
Source frame rate.
integer
Source video width in pixels.
integer
Source video height in pixels.
integer
Number of frames in the source video.
integer
Credits charged for this job. Refunded automatically if the job fails.
string
The requested output format:
webm, mov or mp4.string
The requested output channels:
rgba, rgb or alpha.string
URL of this job, the same one returned as
status_url when the job was created.string | null
Direct download URL of the result on the Poof CDN (
https://cdn.poof.bg/...). null until the job is completed. The link needs no API key, so it can be handed straight to a browser or a <video> tag; anyone with the link can download the file until expires_at. It is served with Content-Disposition: attachment; filename=result.<ext>.string | null
When the result is deleted, 24 hours after completion.
null until the job is completed. After this time result_url stops working and the job itself returns 404 job_not_found.object | null
Set only when
status is failed: an object with code and message describing why the job failed. null otherwise.object | null
Processing statistics. Set once
status is completed; null otherwise. output_width, output_height and processing_seconds are guaranteed; the remaining keys are informational and may change or be removed without notice, so do not build logic on them.Show properties
Show properties
integer
Output video width in pixels: the source size capped at 1920 px on the longest side (or smaller with
size), aspect ratio kept.integer
Output video height in pixels, capped in the same way.
number
Wall-clock time spent processing the job, from
started_at to completed_at.number
Time spent running the matting model on the GPU. Informational, subject to change.
number
Time the pipeline spent waiting for the decoder to deliver frames. Informational, subject to change.
number
Time the pipeline spent waiting for the encoder to accept frames. Informational, subject to change.
integer
Number of scene cuts detected. Each cut resets the temporal stabiliser’s history. Informational, subject to change.
integer
Size of the result file in bytes, handy for showing download progress. Informational, subject to change.
X-Request-ID header.
{
"id": "job_8f3a1c2e9b7d4a10",
"status": "processing",
"progress": 0.42,
"created_at": "2026-10-12T10:12:00Z",
"started_at": "2026-10-12T10:12:04Z",
"completed_at": null,
"duration_seconds": 12.4,
"fps": 29.97,
"width": 1920,
"height": 1080,
"frames": 372,
"credits": 65,
"format": "webm",
"channels": "rgba",
"status_url": "https://api.poof.bg/v1/jobs/job_8f3a1c2e9b7d4a10",
"result_url": null,
"expires_at": null,
"error": null,
"stats": null
}
{
"id": "job_8f3a1c2e9b7d4a10",
"status": "completed",
"progress": 1,
"created_at": "2026-10-12T10:12:00Z",
"started_at": "2026-10-12T10:12:04Z",
"completed_at": "2026-10-12T10:12:41Z",
"duration_seconds": 12.4,
"fps": 29.97,
"width": 1920,
"height": 1080,
"frames": 372,
"credits": 65,
"format": "webm",
"channels": "rgba",
"status_url": "https://api.poof.bg/v1/jobs/job_8f3a1c2e9b7d4a10",
"result_url": "https://cdn.poof.bg/1d/jobs/job_8f3a1c2e9b7d4a10/result.webm",
"expires_at": "2026-10-13T10:12:41Z",
"error": null,
"stats": {
"output_width": 1920,
"output_height": 1080,
"processing_seconds": 37.2,
"matting_seconds": 28.9,
"decode_wait_seconds": 1.4,
"encode_wait_seconds": 3.1,
"scene_cuts": 2,
"output_bytes": 18734211
}
}
{
"id": "job_8f3a1c2e9b7d4a10",
"status": "failed",
"progress": 0.42,
"created_at": "2026-10-12T10:12:00Z",
"started_at": "2026-10-12T10:12:04Z",
"completed_at": "2026-10-12T10:12:20Z",
"duration_seconds": 12.4,
"fps": 29.97,
"width": 1920,
"height": 1080,
"frames": 372,
"credits": 65,
"format": "webm",
"channels": "rgba",
"status_url": "https://api.poof.bg/v1/jobs/job_8f3a1c2e9b7d4a10",
"result_url": null,
"expires_at": null,
"error": {
"code": "processing_failed",
"message": "The model or encoder failed on this video."
},
"stats": null
}
Status lifecycle
queued ──▶ processing ──▶ completed
└─▶ failed
| Status | Meaning |
|---|---|
queued | Accepted and waiting for a worker. progress is 0. |
processing | Frames are being processed. progress increases as batches finish. |
completed | Done. result_url is set and the file is available until expires_at. |
failed | Processing failed. error says why, and the job’s credits have been refunded. |
completed and failed are terminal: a job never leaves them. Every job produces exactly one terminal state.
Polling
Poll every 2 to 5 seconds. Short clips typically finish within a minute; a 5 minute clip can take several minutes. Polling is free and does not count towards your credits.GET requests under /jobs/ are exempt from your plan’s per-organisation rate limit; only the global per-IP limit applies. Polling every 2 to 5 seconds is therefore safe on every plan, but there is no benefit in polling faster than that.
until curl -s https://api.poof.bg/v1/jobs/job_8f3a1c2e9b7d4a10 \
-H "x-api-key: YOUR_API_KEY" | grep -qE '"status": ?"(completed|failed)"'; do
sleep 3
done
import time
import requests
headers = {"x-api-key": "YOUR_API_KEY"}
def wait_for_job(job_id: str, interval: float = 3.0) -> dict:
while True:
r = requests.get(f"https://api.poof.bg/v1/jobs/{job_id}", headers=headers)
r.raise_for_status()
job = r.json()
if job["status"] in ("completed", "failed"):
return job
print(f"{job['status']} {job['progress']:.0%}")
time.sleep(interval)
job = wait_for_job("job_8f3a1c2e9b7d4a10")
if job["status"] == "failed":
raise RuntimeError(f"{job['error']['code']}: {job['error']['message']}")
print("Download from", job["result_url"], "before", job["expires_at"])
const headers = { 'x-api-key': 'YOUR_API_KEY' };
async function waitForJob(jobId: string, intervalMs = 3000) {
while (true) {
const res = await fetch(`https://api.poof.bg/v1/jobs/${jobId}`, { headers });
if (!res.ok) throw new Error(await res.text());
const job = await res.json();
if (job.status === 'completed' || job.status === 'failed') return job;
console.log(job.status, `${Math.round(job.progress * 100)}%`);
await new Promise((r) => setTimeout(r, intervalMs));
}
}
const job = await waitForJob('job_8f3a1c2e9b7d4a10');
if (job.status === 'failed') throw new Error(`${job.error.code}: ${job.error.message}`);
console.log('Download from', job.result_url, 'before', job.expires_at);
Downloading the result
result_url is a direct link to the file on the Poof CDN. It needs no API key, so you can download it from a server, hand it to a browser, or use it as the src of a <video> element. Results can be large (a minute of ProRes 4444 at 1080p is several hundred megabytes), so stream the response to disk rather than buffering it in memory.
The link is valid until expires_at, 24 hours after completion. Copy the file to your own storage if you need it for longer; after expiry the job and its result are deleted and must be submitted again (and billed again).
Error Responses
| Status | Code | Description |
|---|---|---|
| 401 | authentication_error | x-api-key missing or not recognised |
| 404 | job_not_found | No job with this ID, the job belongs to another account, or the job has expired |
curl https://api.poof.bg/v1/jobs/job_8f3a1c2e9b7d4a10 \
-H "x-api-key: YOUR_API_KEY"