CondenseVideoAPI documentation
Contents

CondenseVideo API

Compress, convert, resize, preview, and download video through one job API.

Use the CondenseVideo API to estimate credits, upload videos, start compression jobs, poll progress, preview results, and download smaller files from your app, workflow, or AI agent.

Base URL

Production and local
https://api.condensevideo.com
http://127.0.0.1:8790
APIHTTPS JSON
AuthBearer JWT
JobsAsync or sync
MediaVideo, audio, images

Quickstart

Compress a video in four requests

Use the async job API for browser uploads, SaaS upload pipelines, automation scripts, and agent workflows. CondenseVideo accepts a multipart file, reserves credits, starts processing, and returns URLs for status, preview, and download.

1

Get token

Authenticate with your CondenseVideo access token.

2

Estimate

Call `POST /api/estimate-cost` with file metadata before upload when you want a visible credit quote.

3

Create job

Create an upload with `POST /api/uploads/create`, upload the source to the signed R2 URL, complete it with `POST /api/uploads/complete`, then start rendering with `POST /api/jobs/{job_id}/start-full`.

4

Poll

Call `GET /api/jobs/{job_id}` until the status becomes `complete`, `failed`, or `not_implemented`.

5

Deliver

Request `POST /api/jobs/{job_id}/download-url` to receive a signed output URL after completion.

Create, upload, complete, then start
curl -X POST "https://api.condensevideo.com/api/uploads/create" \
  -H "Authorization: Bearer $CONDENSEVIDEO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"filename":"demo-video.mov","content_type":"video/quicktime","size_bytes":734003200,"tool_slug":"compress-mp4","options":{"profile":"balanced","output_format":"mp4","quality_mode":"quality"}}'

curl -X PUT "$UPLOAD_URL" -H "Content-Type: video/quicktime" --data-binary @./demo-video.mov

curl -X POST "https://api.condensevideo.com/api/uploads/complete" \
  -H "Authorization: Bearer $CONDENSEVIDEO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"job_id":"{job_id}","metadata":{"duration_seconds":180,"height":1080}}'

curl -X POST "https://api.condensevideo.com/api/jobs/{job_id}/start-full" \
  -H "Authorization: Bearer $CONDENSEVIDEO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'
Poll and download
curl -H "Authorization: Bearer $CONDENSEVIDEO_TOKEN" \
  "https://api.condensevideo.com/api/jobs/{job_id}"

curl -L -H "Authorization: Bearer $CONDENSEVIDEO_TOKEN" \
  "https://api.condensevideo.com/api/jobs/{job_id}/download-url" \
  -o compressed.mp4

Authentication

Bearer JWT authentication

API access

Send `Authorization: Bearer` on protected routes

Send the access token issued for your CondenseVideo account. The token identifies the account, credit balance, job ownership, previews, and downloads.

Header
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Token handling

Keep customer tokens out of client logs and source control

Use environment variables on servers and scoped session storage in browser flows. Never place access tokens in query strings, analytics events, public repositories, or downloadable MCP configs.

  • Return clear 401 handling for expired or missing tokens.
  • Attach tokens only to CondenseVideo API requests.
  • Rotate leaked tokens before retrying failed jobs.

Core flow

How a job works

1. Validate

File extension, byte size, video probe metadata, image caps, duration caps, rate limits, and ownership rules are checked before a job is accepted.

2. Reserve credits

CondenseVideo calculates credits from operation type, duration, resolution, and source size. Credits are reserved before processing and returned if the job cannot be completed.

3. Process

The selected tool performs the requested action: compress, convert, resize, crop, trim, mute, extract audio, or optimize images.

4. Report

The result includes input/output paths, bytes, MiB, savings percentage, encode seconds, command, selected encoder, quality fields, and the JSON report path.

5. Preview

Preview creation is not exposed as a separate public route in this build; poll the job and request the signed output URL when complete.

6. Retain

Uploaded originals, previews, outputs, and job records are retained according to your plan and account settings.

Credit pricing

Credit usage, prorata billing, and input-resolution multipliers

CondenseVideo prices media processing by compute work. The baseline is one credit for one minute of normal 1080p compression. CondenseVideo prorates by exact duration in seconds, multiplies by input resolution, multiplies by operation type, then rounds up to a whole-credit debit.

Video credit formula
raw_credits = (duration_seconds / 60) * 1.0 * resolution_multiplier(input_height) * operation_multiplier
credits = max(1, ceil(raw_credits))

# Free estimate and educational operations can return 0 credits.
# If video height is missing, CondenseVideo uses the 1080p multiplier.

Input resolution matters: the multiplier is based on the probed source height. A 4K upload compressed to 1080p still starts at the 4K multiplier because CondenseVideo must process the 4K input.

Resolution multipliers

Input heightMultiplierBaseline rate
360p or below0.45x0.45 credits/min before rounding
480p0.60x0.60 credits/min before rounding
720p0.80x0.80 credits/min before rounding
1080p1.00x1 credit/min baseline
1440p / 2K2.00x2 credits/min before rounding
2160p / 4K4.00x4 credits/min before rounding
4320p / 8K12.00x12 credits/min before rounding
8640p32.00x32 credits/min before rounding
10240p and above45.00x45 credits/min before rounding

Operation multipliers

OperationMultiplierMeaning
Estimate / education0.00xNo media-processing debit.
Video utility0.25xLight utility operation.
Convert video0.80xFormat conversion, usually lighter than full compression.
Compress video / format1.00xNormal compression baseline.
Downscale / platform / device preset1.10xCompression plus delivery-specific constraints.
Social resize or crop1.15xAspect-ratio transformation plus re-encode.
Convert and compress1.35xTwo-step conversion and compression workflow.
Target-size compression1.45xBitrate calculation for a strict output size.
AI image processing2.50xReserved for model-backed image work.

Worked examples

JobCalculationDebitCost interpretation
90 sec 1080p compressionceil(1.5 min x 1.00 x 1.00)2 creditsAt Pro, about
.0104 of included credit value.
20 sec 720p clipceil(0.33 min x 0.80 x 1.00)1 creditMinimum billable media job.
10 min 4K compressionceil(10 min x 4.00 x 1.00)40 credits4K costs 4x normal 1080p compression.
60 min 4K target-size jobceil(60 min x 4.00 x 1.45)348 creditsStrict file-size jobs cost more than normal compression.

Plan and overage cost

Included credits inherit the plan's effective cost per credit. Extra credits cost the current plan credit price plus a 20% premium. API jobs use the same credit ledger as web jobs.

PlanMonthly creditsEffective cost / creditExtra credit price
Creator, $8/mo1,000
.008
.0096
Pro, $26/mo5,000
.0052
.00624
Scale/API, $89/mo17,000
.00524
.0063
Ultimate, $450/mo100,000
.0045
.0054
God Tier, $2,250/mo500,000
.0045
.0054
Movie Production, $9,000/mo3,000,000
.003
.0036

Endpoint reference

API endpoint reference

GET/api/health

Health check plus active API limits.

PublicJSON
Response
{
  "ok": true,
  "service": "condensevideo-api",
  "time": 1783100000.123,
  "limits": {
    "max_upload_mb": 0,
    "retention_hours": 24,
    "requests_per_window": 120,
    "accepted_extensions": [".mp4", ".mov", ".webm"]
  }
}

GET/api/limits

Returns upload, duration, retention, request, and job limits for the current API environment.

PublicJSON

GET/api/tools

Lists available CondenseVideo tool slugs that can be used when creating jobs.

PublicDiscovery
FieldTypeMeaning
slugstringStable tool id passed as `tool_slug`, for example `compress-mp4`, `video-to-mp3`, or `resize-video`.
titlestringHuman-readable product title.
categorystringTool grouping used by the website.
operationstringOperation family used for credit pricing.
credit_modelstringCredit pricing model label for the operation.
compute_tierstring`low`, `medium`, or `high` compute expectation.

GET/api/formats

Returns accepted input extensions, output formats, and quality modes.

PublicDiscovery

GET/api/plans

Returns public pricing plans and credit allowances.

PublicPricing

GET/api/me

Returns the authenticated user record and credit balance.

Bearer token required

POST/api/estimate-cost

Estimates credits from media metadata before you upload or process a file.

JSONCost estimate
Request with metadata
{
  "tool_slug": "compress-mp4",
  "metadata": {
    "duration_seconds": 180,
    "height": 1080,
    "bytes": 734003200
  }
}
Response
{
  "estimate": {
    "tool_slug": "compress-mp4",
    "operation": "compress_video",
    "duration_seconds": 180,
    "height": 1080,
    "source_bytes": 734003200,
    "resolution_multiplier": 1.0,
    "operation_multiplier": 1.0,
    "credits": 3
  }
}

POST/api/uploads/create

Creates a job row and signed R2 upload URL. This endpoint accepts JSON, not multipart file bytes.

Bearer token requiredJSON201 Created
JSON fieldRequiredTypeMeaning
filenameYesstringOriginal source filename used for validation and object-key suffixes.
content_typeYesstringDeclared source MIME type for the signed PUT upload.
size_bytesYesintegerDeclared source byte size. The complete step verifies the uploaded object.
tool_slugNostringDefaults to compress-mp4. Some tools require extra inputs before creation is accepted.
optionsNoobjectCompression, output, trim, resize, crop, image, quality, or required option fields. add-text-to-video requires options.text.
secondary_inputsConditionalarrayRequired for multi-input tools. Each item declares role, filename, content_type, size_bytes, and optional index.

Secondary input roles: merge-video and combine-video need at least one clip; add-audio-to-video and replace-audio-in-video need audio; add-subtitles-to-video needs subtitle; add-watermark-to-video needs watermark. The response returns secondary_uploads with signed URLs for each declared input.

POST/api/uploads/complete

Verifies the uploaded source object, verifies every declared secondary object, stores probed metadata, and estimates credits.

Bearer token requiredJSON
JSON fieldRequiredTypeMeaning
job_idYesstringJob id returned by /api/uploads/create.
metadataNoobjectClient-known media metadata such as duration, width, height, codecs, fps, bitrate, and color metadata.

POST/api/jobs/{job_id}/start-full

Starts the RunPod render after the source upload has been verified.

Bearer token requiredBlocking

GET/api/jobs/{job_id}

Fetches a job record. The job owner must match the authenticated user.

Bearer token requiredPolling

POST/api/jobs/{job_id}/preview-url

Returns a signed preview URL when a preview object exists.

Bearer token requiredBinary

POST/api/jobs/{job_id}/download-url

Returns a signed final output download URL.

Bearer token requiredBinary

Inputs and outputs

Clearly labeled schemas

Job record

FieldTypeDescription
idstringHex UUID-style job id.
tool_slugstringSelected tool slug.
inputstringSource upload identifier.
optionsobjectOptions submitted for the job.
statusstring`queued`, `processing`, `complete`, `failed`, or `not_implemented`.
created_atnumberUnix timestamp.
updated_atnumberUnix timestamp.
user_idstringAuthenticated owner.
upload_validationobjectBytes, extension, original name, duration, width, height, and probe seconds.
credit_estimateobjectCredit estimate used for debit.
credits_reservedintegerCredits debited before processing.
resultobjectOutput report when complete.
downloadstringOutput reference used by the download route.
previewstringPreview reference when a preview exists.
errorstringError detail for failed or unsupported work.

Output report

FieldTypeDescription
tool_slugstringTool that produced the output.
tool_titlestringHuman-readable tool title.
operationstringOperation family.
inputstringSource upload reference.
outputstringOutput file reference.
source_bytesintegerOriginal file size.
output_bytesintegerOutput file size.
source_mibnumberOriginal file size in MiB.
output_mibnumberOutput file size in MiB.
savings_pctnumber|nullSize reduction percentage.
encode_secondsnumberProcessing time.
processing_detailsobjectProcessing details returned when available.
reportstringReport reference returned with the output.
Example complete job response
{
  "id": "f08d9b5ef57c44ee9eb9335f2d2f76aa",
  "tool_slug": "compress-mp4",
  "status": "complete",
  "options": {
    "profile": "balanced",
    "output_format": "mp4",
    "quality_mode": "quality"
  },
  "upload_validation": {
    "bytes": 734003200,
    "original_name": "demo-video.mov",
    "extension": ".mov",
    "duration_seconds": 180.0,
    "width": 1920,
    "height": 1080,
    "probe_seconds": 0.231
  },
  "credit_estimate": {
    "credits": 3,
    "operation": "compress_video",
    "resolution_multiplier": 1.0,
    "operation_multiplier": 1.0
  },
  "result": {
    "source_bytes": 734003200,
    "output_bytes": 289406976,
    "source_mib": 700.0,
    "output_mib": 276.0,
    "savings_pct": 60.57,
    "encoder": "hevc_nvenc",
    "cq": "32",
    "fallback_used": false,
    "encode_seconds": 52.184
  },
  "download_url": "/api/jobs/f08d9b5ef57c44ee9eb9335f2d2f76aa/download-url",
  "preview_url": "/api/jobs/f08d9b5ef57c44ee9eb9335f2d2f76aa/preview-url"
}

Compression options

Options accepted by the processors

OptionUsed byAccepted valuesEffect
profile or compression_profileCompression`balanced`, `small`, `aggressive`, `compatible`, `h264`, `x264`, `av1`, `compact-av1`Selects HEVC NVENC, H.264, AV1, or aggressive compression strategy.
output_format or formatCompress/convert/audio`mp4`, `webm`, `gif`, `mp3`Chooses final container or audio extraction mode.
quality_mode or qualityCompression/GIF/WebM`quality`, `best`, `high`, `hq`, `fast``quality` improves output and usually costs more time. Other values normalize to `fast`.
quality_presetCompression`near_source`, `high`, `quality`, `balanced`, `fast`Maps to CRF/CQ values for H.264, AV1, or HEVC.
target_size_mb, target_mb, or target_sizeTarget size toolsPositive numberComputes bitrate from source duration and target payload size.
widthResize/crop/GIFInteger pixelsOutput width, or GIF width for animated previews.
heightResize/cropInteger pixelsOutput height.
startTrim/GIFSeconds or FFmpeg timestampStart offset for trim or GIF extraction.
duration or lengthTrim/GIFSecondsClip duration.
fpsGIFIntegerGIF frame rate. Defaults to 12 for quality, 8 for fast.
max_edgeImage optimizationInteger pixelsLimits the largest image dimension before saving.
Input videos: mp4, mov, webm, mkv, avi, m4v, wmv, mpeg, mpg, 3gp, ts, m2ts, mts, ogv, flv Images: jpg, jpeg, png, webp, bmp, tif, tiff, heic, heif Audio: mp3, wav, m4a, aac, ogg, flac, opus

Code examples

Current signed-upload flow

The production API does not accept file bytes on /api/jobs. Create a job with JSON, upload the source and any secondary inputs to the signed URLs, complete the upload, then start the render.

Python requests
import time
from pathlib import Path
import requests

BASE_URL = "https://api.condensevideo.com"
TOKEN = "YOUR_SUPABASE_ACCESS_TOKEN"
HEADERS = {"Authorization": f"Bearer {TOKEN}"}

source = Path("demo-video.mov")
created = requests.post(
    f"{BASE_URL}/api/uploads/create",
    headers={**HEADERS, "Content-Type": "application/json"},
    json={
        "filename": source.name,
        "content_type": "video/quicktime",
        "size_bytes": source.stat().st_size,
        "tool_slug": "compress-mp4",
        "options": {"profile": "balanced", "output_format": "mp4", "quality_mode": "quality"},
    },
    timeout=30,
)
created.raise_for_status()
payload = created.json()

with source.open("rb") as file_obj:
    upload = payload["upload"]
    requests.put(upload["url"], headers=upload["headers"], data=file_obj, timeout=600).raise_for_status()

requests.post(
    f"{BASE_URL}/api/uploads/complete",
    headers={**HEADERS, "Content-Type": "application/json"},
    json={"job_id": payload["job"]["id"], "metadata": {"duration_seconds": 180, "height": 1080}},
    timeout=30,
).raise_for_status()

requests.post(f"{BASE_URL}/api/jobs/{payload['job']['id']}/start-full", headers=HEADERS, json={}, timeout=30).raise_for_status()
job_id = payload["job"]["id"]

while True:
    job = requests.get(f"{BASE_URL}/api/jobs/{job_id}", headers=HEADERS, timeout=30).json()
    if job.get("status") == "complete":
        break
    if job.get("status") in {"failed", "not_implemented"}:
        raise RuntimeError(job.get("error_message") or job.get("status"))
    time.sleep(1)

print(requests.post(f"{BASE_URL}/api/jobs/{job_id}/download-url", headers=HEADERS, json={}, timeout=30).json())
JavaScript fetch
const baseUrl = "https://api.condensevideo.com";
const token = "YOUR_SUPABASE_ACCESS_TOKEN";
const file = document.querySelector("input[type=file]").files[0];

const created = await fetch(baseUrl + "/api/uploads/create", {
  method: "POST",
  headers: { Authorization: "Bearer " + token, "Content-Type": "application/json" },
  body: JSON.stringify({
    filename: file.name,
    content_type: file.type || "application/octet-stream",
    size_bytes: file.size,
    tool_slug: "compress-mp4",
    options: { profile: "balanced", output_format: "mp4", quality_mode: "quality" }
  })
});
const payload = await created.json();

await fetch(payload.upload.url, { method: payload.upload.method || "PUT", headers: payload.upload.headers || {}, body: file });
await fetch(baseUrl + "/api/uploads/complete", {
  method: "POST",
  headers: { Authorization: "Bearer " + token, "Content-Type": "application/json" },
  body: JSON.stringify({ job_id: payload.job.id, metadata: { duration_seconds: 180, height: 1080 } })
});
await fetch(baseUrl + "/api/jobs/" + payload.job.id + "/start-full", {
  method: "POST",
  headers: { Authorization: "Bearer " + token, "Content-Type": "application/json" },
  body: "{}"
});
Secondary input example
{
  "filename": "talk.mov",
  "content_type": "video/quicktime",
  "size_bytes": 734003200,
  "tool_slug": "add-subtitles-to-video",
  "secondary_inputs": [{"role":"subtitle","filename":"captions.vtt","content_type":"text/vtt","size_bytes":42144}]
}

// Upload the source to response.upload.url.
// Upload captions.vtt to response.secondary_uploads[0].upload.url.
// Then call /api/uploads/complete and /api/jobs/{job_id}/start-full.
Raw HTTP JSON shape
POST /api/uploads/create HTTP/1.1
Host: api.condensevideo.com
Authorization: Bearer YOUR_SUPABASE_ACCESS_TOKEN
Content-Type: application/json

{"filename":"demo-video.mov","content_type":"video/quicktime","size_bytes":734003200,"tool_slug":"compress-mp4","options":{"profile":"balanced","output_format":"mp4","quality_mode":"quality"}}

MCP agent setup

Let an AI agent compress videos for you

MCP means Model Context Protocol. A local MCP server can expose CondenseVideo tools to an agent so the agent can estimate cost, start a compression job, poll status, and download the output without the user manually opening the website.

Recommended tools
  • condensevideo_estimate: estimate credits from known metadata.
  • condensevideo_compress: upload a local file and start an async job.
  • condensevideo_status: read job status and output metrics.
  • condensevideo_download: save the compressed output to your chosen download folder.
Environment
Agent connector environment
CONDENSEVIDEO_API_BASE=https://api.condensevideo.com
CONDENSEVIDEO_TOKEN=your_supabase_access_token
CONDENSEVIDEO_DOWNLOAD_DIR=./condensevideo-downloads
Python MCP server skeleton
# server.py
import os
import json
import time
from pathlib import Path
import requests
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("condensevideo")
BASE_URL = os.environ.get("CONDENSEVIDEO_API_BASE", "https://api.condensevideo.com")
TOKEN = os.environ["CONDENSEVIDEO_TOKEN"]
DOWNLOAD_DIR = Path(os.environ.get("CONDENSEVIDEO_DOWNLOAD_DIR", ".")).resolve()

def headers():
    return {"Authorization": f"Bearer {TOKEN}"}

@mcp.tool()
def condensevideo_estimate(tool_slug: str, duration_seconds: float, height: int, bytes: int) -> dict:
    """Estimate CondenseVideo credits for a media job."""
    payload = {
        "tool_slug": tool_slug,
        "metadata": {
            "duration_seconds": duration_seconds,
            "height": height,
            "bytes": bytes,
        },
    }
    response = requests.post(f"{BASE_URL}/api/estimate-cost", json=payload, timeout=30)
    response.raise_for_status()
    return response.json()["estimate"]

@mcp.tool()
def condensevideo_compress(path: str, tool_slug: str = "compress-mp4", profile: str = "balanced", output_format: str = "mp4", quality_mode: str = "quality") -> dict:
    """Upload a local video and start a CondenseVideo async job."""
    source = Path(path).expanduser().resolve()
    if not source.exists():
        raise FileNotFoundError(str(source))
    options = {
        "profile": profile,
        "output_format": output_format,
        "quality_mode": quality_mode,
    }
    created = requests.post(
        f"{BASE_URL}/api/uploads/create",
        headers={**headers(), "Content-Type": "application/json"},
        json={
            "filename": source.name,
            "content_type": "video/quicktime",
            "size_bytes": source.stat().st_size,
            "tool_slug": tool_slug,
            "options": options,
        },
        timeout=30,
    )
    created.raise_for_status()
    payload = created.json()
    with source.open("rb") as file_obj:
        upload = payload["upload"]
        requests.put(upload["url"], headers=upload["headers"], data=file_obj, timeout=600).raise_for_status()
    requests.post(
        f"{BASE_URL}/api/uploads/complete",
        headers={**headers(), "Content-Type": "application/json"},
        json={"job_id": payload["job"]["id"], "metadata": {}},
        timeout=30,
    ).raise_for_status()
    started = requests.post(f"{BASE_URL}/api/jobs/{payload['job']['id']}/start-full", headers=headers(), json={}, timeout=30)
    started.raise_for_status()
    return started.json()

@mcp.tool()
def condensevideo_status(job_id: str) -> dict:
    """Return the current status and report fields for a job."""
    response = requests.get(f"{BASE_URL}/api/jobs/{job_id}", headers=headers(), timeout=30)
    response.raise_for_status()
    return response.json()

@mcp.tool()
def condensevideo_download(job_id: str, filename: str = "compressed-output") -> str:
    """Download the completed output to your configured download folder."""
    DOWNLOAD_DIR.mkdir(parents=True, exist_ok=True)
    response = requests.get(f"{BASE_URL}/api/jobs/{job_id}/download-url", headers=headers(), timeout=600)
    response.raise_for_status()
    content_type = response.headers.get("Content-Type", "")
    suffix = ".mp4"
    if "webm" in content_type:
        suffix = ".webm"
    elif "gif" in content_type:
        suffix = ".gif"
    elif "mpeg" in content_type or "mp3" in content_type:
        suffix = ".mp3"
    target = DOWNLOAD_DIR / (Path(filename).stem + suffix)
    target.write_bytes(response.content)
    return str(target)

if __name__ == "__main__":
    mcp.run()

Important: install the MCP Python package and `requests`, then register the server with your MCP-compatible client. Keep the API token out of source control.

Install dependencies
python -m venv .venv
.venv\Scripts\activate
pip install "mcp[cli]" requests
Example client config
{
  "mcpServers": {
    "condensevideo": {
      "command": "python",
      "args": ["./condensevideo_mcp_server.py"],
      "env": {
        "CONDENSEVIDEO_API_BASE": "https://api.condensevideo.com",
        "CONDENSEVIDEO_TOKEN": "YOUR_TOKEN",
        "CONDENSEVIDEO_DOWNLOAD_DIR": "./condensevideo-downloads"
      }
    }
  }
}

Agent instruction example: "Use `condensevideo_compress` with `profile=small` and `quality_mode=quality` on this video. Poll with `condensevideo_status` until complete, download the result, then report original size, compressed size, savings percentage, encoder, credits used, and output path."

Errors and limits

Failure modes your integration should handle

Error response shape

JSON error
{
  "error": "Authorization bearer token is required",
  "status": 401
}

Statuses

  • 400: missing `tool_slug`, invalid options, unsupported extension, missing input.
  • 401: missing/invalid token, insufficient credits, rate limit, job ownership mismatch.
  • 404: job, preview, download, or route not found.
  • 413: upload exceeds configured byte cap.
  • 500: unexpected API error.
LimitDefaultIntegration advice
Request rate120 requests per 60 seconds per client keyBack off on "Rate limit exceeded. Try again later."
Job rate30 jobs per 3600 seconds per userBatch responsibly and queue large automation work.
Concurrent jobs250 active jobs per userKeep a client-side queue even though the server limit is generous.
Retention24 hoursDownload outputs promptly and store them in your own system.
Command timeout7200 secondsUse async jobs for long videos; do not keep browser requests open.