Tasks

GET /api/v1/tasks/{id}, /tasks/{id}/events and /tasks — check a task's status, stream its progress, download the result, list past tasks.

Get a task

GET https://videoupscaler.co/api/v1/tasks/{id}

Returns the task with its latest status. Poll it every 5–10 seconds while it is queued or processing. Longer videos and higher resolutions take longer.

curl https://videoupscaler.co/api/v1/tasks/0b6f3c1e-8a52-5d1e-9c43-3f1d2e7a9b10 \
  -H "Authorization: Bearer $VIDEOUPSCALER_API_KEY"
{
  "id": "0b6f3c1e-8a52-5d1e-9c43-3f1d2e7a9b10",
  "status": "completed",
  "resolution": "2k",
  "video_url": "https://example.com/clip.mp4",
  "duration_seconds": 12.4,
  "processed_seconds": 12.4,
  "credits_charged": 26,
  "watermark": false,
  "output_url": "https://…/clip_upscaled.mp4",
  "thumbnail_url": "https://…/clip_first_frame.jpg",
  "error": null,
  "created_at": "2026-09-30T08:00:00.000Z",
  "updated_at": "2026-09-30T08:03:41.000Z"
}

A task that doesn't exist, or belongs to another account, returns 404 not_found.

Polling example

async function waitForTask(id) {
  while (true) {
    const res = await fetch(`https://videoupscaler.co/api/v1/tasks/${id}`, {
      headers: { Authorization: `Bearer ${process.env.VIDEOUPSCALER_API_KEY}` },
    });
    const task = await res.json();
    if (task.status === 'completed') return task.output_url;
    if (task.status === 'failed') throw new Error(task.error);
    await new Promise((r) => setTimeout(r, 8000));
  }
}

Stream task events

GET https://videoupscaler.co/api/v1/tasks/{id}/events

Instead of polling, you can hold one connection open and receive the task's progress as Server-Sent Events. The request answers with a 302 redirect to a signed stream URL; SSE clients follow it automatically (with curl, add -L). The signed URL already grants access, so your API key isn't sent to it.

EventDataWhen
status{"id": "…", "status": "queued"}The job is waiting for a GPU (queued) or running (processing). Sent when it changes.
completedThe full taskThe result is ready. The stream then closes.
failedThe full taskProcessing failed and the credits were refunded. The stream then closes.
timeout{"id": "…"}The stream closes after 10 minutes. Open /events again if the task is still running.

The data of completed and failed is exactly what GET /tasks/{id} returns. Opening the stream for a task that already finished sends that event right away.

curl -N -L https://videoupscaler.co/api/v1/tasks/TASK_ID/events \
  -H "Authorization: Bearer $VIDEOUPSCALER_API_KEY"
event: status
data: {"id":"0b6f3c1e-…","status":"queued"}

event: status
data: {"id":"0b6f3c1e-…","status":"processing"}

event: completed
data: {"id":"0b6f3c1e-…","status":"completed","output_url":"https://…","…":"…"}
// npm install eventsource
import { EventSource } from 'eventsource';

const events = new EventSource(`https://videoupscaler.co/api/v1/tasks/${taskId}/events`, {
  fetch: (url, init) =>
    fetch(url, {
      ...init,
      headers: { ...init.headers, Authorization: `Bearer ${process.env.VIDEOUPSCALER_API_KEY}` },
    }),
});
events.addEventListener('status', (e) => console.log(JSON.parse(e.data).status));
events.addEventListener('completed', (e) => {
  console.log('done:', JSON.parse(e.data).output_url);
  events.close();
});
events.addEventListener('failed', (e) => {
  console.error(JSON.parse(e.data).error);
  events.close();
});
# pip install httpx httpx-sse
import json, os, httpx
from httpx_sse import connect_sse

headers = {"Authorization": f"Bearer {os.environ['VIDEOUPSCALER_API_KEY']}"}
url = f"https://videoupscaler.co/api/v1/tasks/{task_id}/events"
with httpx.Client(follow_redirects=True, timeout=None) as client:
    with connect_sse(client, "GET", url, headers=headers) as source:
        for event in source.iter_sse():
            task = json.loads(event.data)
            print(event.event, task["status"])
            if event.event in ("completed", "failed", "timeout"):
                break

Polling GET /tasks/{id} gives the same result; use whichever fits your code.

List tasks

GET https://videoupscaler.co/api/v1/tasks?limit=20&page=1
QueryDefaultDescription
limit20Tasks per page, 1–100
page1Page number, starting at 1

Returns tasks newest first. The list includes tasks created on the website, too.

{
  "data": [{ "id": "…", "status": "completed", "…": "…" }],
  "page": 1,
  "limit": 20,
  "total": 42,
  "has_more": true
}

The task object

FieldTypeDescription
idstringTask id
statusstringqueued, processing, completed or failed
resolutionstring1080p, 2k or 4k
video_urlstringThe video you sent
duration_secondsnumberLength of the input video, read from the file
processed_secondsnumberHow many seconds are upscaled. Equal to duration_seconds for API tasks; shorter only for tasks created on the website while the account was free.
credits_chargedintegerCredits charged for this task (refunded if it fails)
watermarkbooleanWhether the result carries a watermark. Always false for API tasks; true only for tasks created on the website while the account was free.
output_urlstring | nullThe upscaled video once completed
thumbnail_urlstring | nullFirst frame of the result once completed
errorstring | nullWhy the task failed, when failed
created_atstringISO 8601 time
updated_atstringISO 8601 time

Download output_url soon after the task completes and keep your own copy. Don't rely on it as permanent storage.