Video Enhancement API Documentation

The Unblur Video API lets you enhance videos programmatically: upload a file (or point us at a URL), create an upscale job, poll until it completes, and download the result.

Base URL: https://unblurvideo.ai/api/v1

GET https://unblurvideo.ai/api/v1 is an unauthenticated discovery endpoint that returns the API version, documentation URL, OpenAPI description, and resource paths. All resource endpoints below require authentication.

Requests and responses are JSON unless a presigned upload URL explicitly asks for raw file bytes. JSON responses share one envelope:

{ "success": true, "data": { } }

Failed requests return success: false with an error message and a machine-readable code:

{ "success": false, "error": "Insufficient credits.", "code": "INSUFFICIENT_CREDITS" }

API access is included in the Pro plan. Requests from accounts without an active Pro subscription are rejected with 403 API_ACCESS_REQUIRED. See pricing.


Authentication

Create an API key in your dashboard, then pass it as a Bearer token on every request:

curl -H "Authorization: Bearer ubv_your_key_here" https://unblurvideo.ai/api/v1/credits

Keys start with ubv_ and are shown only once at creation time. Treat them like passwords: store them in your server's environment, never in client-side code or a repository. You can revoke a key at any time from the dashboard.

A missing or invalid key returns 401 with a standard WWW-Authenticate: Bearer ... challenge. API responses use Cache-Control: no-store and are marked X-Robots-Tag: noindex, nofollow; the human-readable page you are reading remains the canonical, indexable documentation.


Quick start

The fastest integration passes a public video URL — we download it, analyze it, and it's ready for a job in one call:

curl -X POST -H "Authorization: Bearer ubv_..." -H "Content-Type: application/json" -d '{"mode": "source_url", "source_url": "https://example.com/video.mp4"}' https://unblurvideo.ai/api/v1/uploads

Then create a job with the returned uploadId, and poll it until status is completed:

curl -X POST -H "Authorization: Bearer ubv_..." -H "Idempotency-Key: 7f7f2f78-3cb4-4e2d-92e2-5d7dc7cf70f9" -H "Content-Type: application/json" -d '{"upload_id": "UPLOAD_ID", "target_resolution": "1080p"}' https://unblurvideo.ai/api/v1/jobs
curl -H "Authorization: Bearer ubv_..." https://unblurvideo.ai/api/v1/jobs/JOB_ID

Once the completed output has been persisted to our storage, the response includes a temporary outputUrl you can download the enhanced video from.


Endpoints

POST /uploads

Register a video to process. Two modes:

Mode 1 — source_url. We fetch a public https URL server-side (max 500 MB; mp4, mov or webm). Metadata is extracted before the call returns, so you can create a job immediately.

curl -X POST -H "Authorization: Bearer ubv_..." -H "Content-Type: application/json" -d '{"mode": "source_url", "source_url": "https://example.com/video.mp4"}' https://unblurvideo.ai/api/v1/uploads
{
  "success": true,
  "data": {
    "uploadId": "0c9d…",
    "status": "uploaded",
    "sizeBytes": 10485760,
    "contentType": "video/mp4",
    "metadata": { "resolution": "720p", "width": 1280, "height": 720, "duration": 12.4, "fps": 30 },
    "metadataStatus": "ready"
  }
}

Mode 2 — presigned upload. For local files: request an upload URL, PUT the file to it, then confirm.

curl -X POST -H "Authorization: Bearer ubv_..." -H "Content-Type: application/json" -d '{"filename": "clip.mp4", "content_type": "video/mp4", "size_bytes": 10485760}' https://unblurvideo.ai/api/v1/uploads

The response contains uploadId and a temporary uploadUrl (valid 10 minutes). Upload the raw file bytes to it:

{
  "success": true,
  "data": {
    "uploadId": "0c9d…",
    "uploadUrl": "https://…",
    "expiresInSeconds": 600
  }
}
curl -X PUT -H "Content-Type: video/mp4" --data-binary @clip.mp4 "UPLOAD_URL"

POST /uploads/{id}/confirm

Confirm a presigned upload after the PUT succeeds. Verifies the stored file and extracts video metadata synchronously (this call can take a few seconds).

curl -X POST -H "Authorization: Bearer ubv_..." https://unblurvideo.ai/api/v1/uploads/UPLOAD_ID/confirm

The response uses the same public upload fields as source_url mode:

{
  "success": true,
  "data": {
    "uploadId": "0c9d…",
    "status": "uploaded",
    "sizeBytes": 10485760,
    "metadata": { "resolution": "720p", "width": 1280, "height": 720, "duration": 12.4, "fps": 30 },
    "metadataStatus": "ready"
  }
}

If metadataStatus is "failed", the video could not be analyzed — try a different file.

POST /jobs

Create an enhancement job for an uploaded video.

FieldTypeDescription
upload_idstringRequired. The uploadId from an upload call.
target_resolutionstringRequired. 1080p, 2k or 4k.
previewbooleanOptional. true renders a free ~3-second watermarked preview.
curl -X POST \
  -H "Authorization: Bearer ubv_..." \
  -H "Idempotency-Key: 7f7f2f78-3cb4-4e2d-92e2-5d7dc7cf70f9" \
  -H "Content-Type: application/json" \
  -d '{"upload_id": "UPLOAD_ID", "target_resolution": "1080p"}' \
  https://unblurvideo.ai/api/v1/jobs

Returns 201 with the job, including its creditCost. Credits are deducted when the job is created and automatically refunded if processing fails. If an identical job is already active, the existing job is returned with 200 and "duplicate": true — no double charge.

If metadata extraction is still in progress you get 409 METADATA_PENDING with a Retry-After header; retry after that many seconds.

Idempotent retries

Send a unique Idempotency-Key header when creating a job (1–128 characters; a UUID is a good choice). The key is scoped to your account:

  • Repeating the same key with the same request returns the original job with 200 and "duplicate": true, even if the first HTTP response was lost.
  • Reusing that key with different upload_id, target_resolution, or preview values returns 409 IDEMPOTENCY_KEY_REUSED.
  • The raw key is never stored; the server stores SHA-256 hashes of the key and normalized request.

Idempotency-Key is optional for backward compatibility, but strongly recommended for every production integration.

GET /jobs/{id}

Poll a job. Polling this endpoint also drives processing bookkeeping, so poll it (every 5–10 seconds is plenty) rather than scraping other state.

curl -H "Authorization: Bearer ubv_..." https://unblurvideo.ai/api/v1/jobs/JOB_ID

Key response fields:

FieldDescription
statuspending, in_queue, processing, completed, failed or cancelled.
outputUrlPresent after the completed output is persisted: a temporary download URL (valid 4 hours). Re-poll for a fresh one. Provider URLs are never exposed.
creditCostCredits charged for this job.
errorMessagePresent when failed. Failed jobs are refunded automatically.

The public response intentionally excludes internal R2 object keys, RunPod identifiers, provider payloads, and worker metadata.

GET /jobs

List your jobs, newest first. Query parameters: limit (default 20, max 100), offset, and optional status filter.

curl -H "Authorization: Bearer ubv_..." "https://unblurvideo.ai/api/v1/jobs?status=completed&limit=10"

GET /credits

Check your credit balance and plan.

curl -H "Authorization: Bearer ubv_..." https://unblurvideo.ai/api/v1/credits
{
  "success": true,
  "data": {
    "totalCredits": 3600,
    "subscriptionCredits": 3400,
    "oneTimeCredits": 200,
    "planName": "Pro",
    "subscriptionStatus": "active",
    "currentPeriodEnd": "2026-08-01T00:00:00.000Z"
  }
}

Credits and pricing

Jobs are billed in credits based on video duration and target resolution:

Target resolutionCredits per second of video
1080p1.0
2k1.6
4k3.23

The total cost is first calculated from the full video duration and exact rate to two decimal places, then rounded up once to the next whole credit for both display and charging. For example, 10 seconds at the 4k rate is 32.30 credits, charged as 33. Previews are free. Source videos may be at most 1080p and 500 MB. Credits come with your subscription and can be topped up with one-time packs from the pricing page.


Rate limits

Limits apply per account (all keys share them). When exceeded, requests return 429 with a Retry-After header.

ScopeLimit
Uploads (presigned or confirm)30 / hour each
Uploads via source_url10 / hour
Job creation45 / hour
Polling (job status, job list, credits)120 / minute

Errors

HTTPCodeMeaning
400INVALID_JSON, INVALID_INPUT, INVALID_IDEMPOTENCY_KEY, INVALID_SOURCE_URL, UNSUPPORTED_TARGET_RESOLUTIONThe request syntax, fields, ID, query, URL, target resolution, or idempotency header is invalid.
400UNSUPPORTED_FILE_EXTENSION, UNSUPPORTED_FILE_TYPE, FILE_TYPE_EXTENSION_MISMATCHThe declared filename and MIME type are unsupported or do not match.
400FILE_SIZE_REQUIRED, FILE_SIZE_UNVERIFIED, FILE_SIZE_MISMATCH, FILE_TYPE_UNVERIFIED, FILE_TYPE_MISMATCH, FILE_NOT_FOUND, INVALID_OBJECT_KEY, UPLOAD_NOT_READYThe stored upload could not be verified or is not ready for processing.
401MISSING_API_KEY, INVALID_API_KEY, KEY_EXPIREDMissing, wrong or revoked key.
402INSUFFICIENT_CREDITSNot enough credits for this job.
403API_ACCESS_REQUIRED, USER_BANNEDYour plan does not include API access or the account is unavailable.
404UPLOAD_NOT_FOUND, JOB_NOT_FOUND, USAGE_NOT_FOUNDResource or billing record does not exist or is not yours.
409METADATA_PENDINGVideo still being analyzed; retry after Retry-After seconds.
409UPLOAD_STATUS_CONFLICT, JOB_ALREADY_CREATING, IDEMPOTENCY_KEY_REUSEDState or idempotency conflict.
413FILE_TOO_LARGEVideo exceeds 500 MB.
422METADATA_UNAVAILABLE, RESOLUTION_TOO_HIGHThe source video cannot be analyzed or exceeds the supported input resolution.
422SOURCE_URL_FORBIDDEN, SOURCE_URL_UNRESOLVABLE, SOURCE_URL_TOO_MANY_REDIRECTSThe remote URL is unsafe, cannot resolve, or redirects too many times.
429RATE_LIMIT_EXCEEDEDSlow down; respect Retry-After.
500 / 503INTERNAL_SERVER_ERROR, RUNPOD_FAILED, IDEMPOTENCY_STORAGE_FAILED, SERVER_MISCONFIGURED, SERVICE_UNAVAILABLETemporary server or processing backend failure; charged credits are refunded when applicable.
502 / 504SOURCE_FETCH_FAILED, SOURCE_FETCH_TIMEOUTWe couldn't download your source_url.