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
https://api.condensevideo.com
http://127.0.0.1:8790
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.
Get token
Authenticate with your CondenseVideo access token.
Estimate
Call `POST /api/estimate-cost` with file metadata before upload when you want a visible credit quote.
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`.
Poll
Call `GET /api/jobs/{job_id}` until the status becomes `complete`, `failed`, or `not_implemented`.
Deliver
Request `POST /api/jobs/{job_id}/download-url` to receive a signed output URL after completion.
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 '{}'
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
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.
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
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
File extension, byte size, video probe metadata, image caps, duration caps, rate limits, and ownership rules are checked before a job is accepted.
CondenseVideo calculates credits from operation type, duration, resolution, and source size. Credits are reserved before processing and returned if the job cannot be completed.
The selected tool performs the requested action: compress, convert, resize, crop, trim, mute, extract audio, or optimize images.
The result includes input/output paths, bytes, MiB, savings percentage, encode seconds, command, selected encoder, quality fields, and the JSON report path.
Preview creation is not exposed as a separate public route in this build; poll the job and request the signed output URL when complete.
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.
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 height | Multiplier | Baseline rate |
|---|---|---|
| 360p or below | 0.45x | 0.45 credits/min before rounding |
| 480p | 0.60x | 0.60 credits/min before rounding |
| 720p | 0.80x | 0.80 credits/min before rounding |
| 1080p | 1.00x | 1 credit/min baseline |
| 1440p / 2K | 2.00x | 2 credits/min before rounding |
| 2160p / 4K | 4.00x | 4 credits/min before rounding |
| 4320p / 8K | 12.00x | 12 credits/min before rounding |
| 8640p | 32.00x | 32 credits/min before rounding |
| 10240p and above | 45.00x | 45 credits/min before rounding |
Operation multipliers
| Operation | Multiplier | Meaning |
|---|---|---|
| Estimate / education | 0.00x | No media-processing debit. |
| Video utility | 0.25x | Light utility operation. |
| Convert video | 0.80x | Format conversion, usually lighter than full compression. |
| Compress video / format | 1.00x | Normal compression baseline. |
| Downscale / platform / device preset | 1.10x | Compression plus delivery-specific constraints. |
| Social resize or crop | 1.15x | Aspect-ratio transformation plus re-encode. |
| Convert and compress | 1.35x | Two-step conversion and compression workflow. |
| Target-size compression | 1.45x | Bitrate calculation for a strict output size. |
| AI image processing | 2.50x | Reserved for model-backed image work. |
Worked examples
| Job | Calculation | Debit | Cost interpretation |
|---|---|---|---|
| 90 sec 1080p compression | ceil(1.5 min x 1.00 x 1.00) | 2 credits | At Pro, about |
| 20 sec 720p clip | ceil(0.33 min x 0.80 x 1.00) | 1 credit | Minimum billable media job. |
| 10 min 4K compression | ceil(10 min x 4.00 x 1.00) | 40 credits | 4K costs 4x normal 1080p compression. |
| 60 min 4K target-size job | ceil(60 min x 4.00 x 1.45) | 348 credits | Strict 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.
| Plan | Monthly credits | Effective cost / credit | Extra credit price |
|---|---|---|---|
| Creator, $8/mo | 1,000 | | |
| Pro, $26/mo | 5,000 | | |
| Scale/API, $89/mo | 17,000 | | |
| Ultimate, $450/mo | 100,000 | | |
| God Tier, $2,250/mo | 500,000 | | |
| Movie Production, $9,000/mo | 3,000,000 | | |
Endpoint reference
API endpoint reference
GET/api/health
Health check plus active API limits.
{
"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.
GET/api/tools
Lists available CondenseVideo tool slugs that can be used when creating jobs.
| Field | Type | Meaning |
|---|---|---|
slug | string | Stable tool id passed as `tool_slug`, for example `compress-mp4`, `video-to-mp3`, or `resize-video`. |
title | string | Human-readable product title. |
category | string | Tool grouping used by the website. |
operation | string | Operation family used for credit pricing. |
credit_model | string | Credit pricing model label for the operation. |
compute_tier | string | `low`, `medium`, or `high` compute expectation. |
GET/api/formats
Returns accepted input extensions, output formats, and quality modes.
GET/api/plans
Returns public pricing plans and credit allowances.
GET/api/me
Returns the authenticated user record and credit balance.
POST/api/estimate-cost
Estimates credits from media metadata before you upload or process a file.
{
"tool_slug": "compress-mp4",
"metadata": {
"duration_seconds": 180,
"height": 1080,
"bytes": 734003200
}
}{
"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.
| JSON field | Required | Type | Meaning |
|---|---|---|---|
filename | Yes | string | Original source filename used for validation and object-key suffixes. |
content_type | Yes | string | Declared source MIME type for the signed PUT upload. |
size_bytes | Yes | integer | Declared source byte size. The complete step verifies the uploaded object. |
tool_slug | No | string | Defaults to compress-mp4. Some tools require extra inputs before creation is accepted. |
options | No | object | Compression, output, trim, resize, crop, image, quality, or required option fields. add-text-to-video requires options.text. |
secondary_inputs | Conditional | array | Required 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.
| JSON field | Required | Type | Meaning |
|---|---|---|---|
job_id | Yes | string | Job id returned by /api/uploads/create. |
metadata | No | object | Client-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.
GET/api/jobs/{job_id}
Fetches a job record. The job owner must match the authenticated user.
POST/api/jobs/{job_id}/preview-url
Returns a signed preview URL when a preview object exists.
POST/api/jobs/{job_id}/download-url
Returns a signed final output download URL.
Inputs and outputs
Clearly labeled schemas
Job record
| Field | Type | Description |
|---|---|---|
id | string | Hex UUID-style job id. |
tool_slug | string | Selected tool slug. |
input | string | Source upload identifier. |
options | object | Options submitted for the job. |
status | string | `queued`, `processing`, `complete`, `failed`, or `not_implemented`. |
created_at | number | Unix timestamp. |
updated_at | number | Unix timestamp. |
user_id | string | Authenticated owner. |
upload_validation | object | Bytes, extension, original name, duration, width, height, and probe seconds. |
credit_estimate | object | Credit estimate used for debit. |
credits_reserved | integer | Credits debited before processing. |
result | object | Output report when complete. |
download | string | Output reference used by the download route. |
preview | string | Preview reference when a preview exists. |
error | string | Error detail for failed or unsupported work. |
Output report
| Field | Type | Description |
|---|---|---|
tool_slug | string | Tool that produced the output. |
tool_title | string | Human-readable tool title. |
operation | string | Operation family. |
input | string | Source upload reference. |
output | string | Output file reference. |
source_bytes | integer | Original file size. |
output_bytes | integer | Output file size. |
source_mib | number | Original file size in MiB. |
output_mib | number | Output file size in MiB. |
savings_pct | number|null | Size reduction percentage. |
encode_seconds | number | Processing time. |
processing_details | object | Processing details returned when available. |
report | string | Report reference returned with the output. |
{
"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
| Option | Used by | Accepted values | Effect |
|---|---|---|---|
profile or compression_profile | Compression | `balanced`, `small`, `aggressive`, `compatible`, `h264`, `x264`, `av1`, `compact-av1` | Selects HEVC NVENC, H.264, AV1, or aggressive compression strategy. |
output_format or format | Compress/convert/audio | `mp4`, `webm`, `gif`, `mp3` | Chooses final container or audio extraction mode. |
quality_mode or quality | Compression/GIF/WebM | `quality`, `best`, `high`, `hq`, `fast` | `quality` improves output and usually costs more time. Other values normalize to `fast`. |
quality_preset | Compression | `near_source`, `high`, `quality`, `balanced`, `fast` | Maps to CRF/CQ values for H.264, AV1, or HEVC. |
target_size_mb, target_mb, or target_size | Target size tools | Positive number | Computes bitrate from source duration and target payload size. |
width | Resize/crop/GIF | Integer pixels | Output width, or GIF width for animated previews. |
height | Resize/crop | Integer pixels | Output height. |
start | Trim/GIF | Seconds or FFmpeg timestamp | Start offset for trim or GIF extraction. |
duration or length | Trim/GIF | Seconds | Clip duration. |
fps | GIF | Integer | GIF frame rate. Defaults to 12 for quality, 8 for fast. |
max_edge | Image optimization | Integer pixels | Limits the largest image dimension before saving. |
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.
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())
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: "{}"
});
{
"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.
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.
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.
CONDENSEVIDEO_API_BASE=https://api.condensevideo.com
CONDENSEVIDEO_TOKEN=your_supabase_access_token
CONDENSEVIDEO_DOWNLOAD_DIR=./condensevideo-downloads
# 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.
python -m venv .venv
.venv\Scripts\activate
pip install "mcp[cli]" requests
{
"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
{
"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.
| Limit | Default | Integration advice |
|---|---|---|
| Request rate | 120 requests per 60 seconds per client key | Back off on "Rate limit exceeded. Try again later." |
| Job rate | 30 jobs per 3600 seconds per user | Batch responsibly and queue large automation work. |
| Concurrent jobs | 250 active jobs per user | Keep a client-side queue even though the server limit is generous. |
| Retention | 24 hours | Download outputs promptly and store them in your own system. |
| Command timeout | 7200 seconds | Use async jobs for long videos; do not keep browser requests open. |