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}/eventsInstead 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.
| Event | Data | When |
|---|---|---|
status | {"id": "…", "status": "queued"} | The job is waiting for a GPU (queued) or running (processing). Sent when it changes. |
completed | The full task | The result is ready. The stream then closes. |
failed | The full task | Processing 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"):
breakPolling 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| Query | Default | Description |
|---|---|---|
limit | 20 | Tasks per page, 1–100 |
page | 1 | Page 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
| Field | Type | Description |
|---|---|---|
id | string | Task id |
status | string | queued, processing, completed or failed |
resolution | string | 1080p, 2k or 4k |
video_url | string | The video you sent |
duration_seconds | number | Length of the input video, read from the file |
processed_seconds | number | How 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_charged | integer | Credits charged for this task (refunded if it fails) |
watermark | boolean | Whether the result carries a watermark. Always false for API tasks; true only for tasks created on the website while the account was free. |
output_url | string | null | The upscaled video once completed |
thumbnail_url | string | null | First frame of the result once completed |
error | string | null | Why the task failed, when failed |
created_at | string | ISO 8601 time |
updated_at | string | ISO 8601 time |
Download output_url soon after the task completes and keep your own copy. Don't rely on it as permanent storage.