---
title: "Upload a video file (multipart)"
method: POST
path: "/api/videos"
tags: ["videos"]
---

# Upload a video file (multipart)

`POST /api/videos`

## Request

- Base URL: `https://cliponit.app`
- URL: `https://cliponit.app/api/videos`
- Auth: one of:
  - HTTP bearer
  - API key in cookie `session`
  - OAuth 2 (authorization code, authorization URL `https://cliponit.app/oauth/authorize`, token URL `https://cliponit.app/oauth/token`, scopes: `clips`)

Field `file`: mp4/mov/webm, streamed to disk, ≤ `quota.max_bytes` of the plan (3 GB free … 20 GB pro). Checks **before** receiving the body: 402 if the monthly video quota is reached or `Content-Length` exceeds the plan; free plan also refuses videos longer than 1200 s (20 min). Returns the video with `status: processing`; processing = probing → transcribing → detecting, usually 1-5 min for a 20 min video. Then call `getVideo` to read the clips.

## Response `200`

Created video (processing)

- Video
  - `id` string, uuid, required
  - `title` string, required
  - `path` string: Server-side file path (informational)
  - `url` string, required: Source file, relative to the server, e.g. `/files/uploads/<id>.mp4`
  - `thumbnail_url` string, nullable
  - `thumbnail_path` string, nullable
  - `size_bytes` integer, required
  - `duration_s` number, nullable: Seconds, null until probed
  - `width` integer, nullable
  - `height` integer, nullable
  - `status` 'processing' | 'ready' | 'failed', required
  - `state` 'queued' | 'running' | 'ready' | 'failed', required: `processing` split by the job queue: `queued` (waiting behind other videos) or `running`; otherwise = `status`
  - `queue_position` integer, nullable: 1-based position in the processing queue while `state` is `queued`
  - `eta_s` number, nullable: Estimated seconds until `ready` (0.25 × (duration + durations of the videos ahead)); null when not processing
  - `progress` 'downloading' | 'probing' | 'transcribing' | 'detecting' | 'demo' | 'null', nullable: Current step while processing; null when ready/failed. `demo` = demo copy waiting for the master
  - `error` string, nullable: Why it failed
  - `is_demo` 0 | 1, required: 1 = the sample video seeded at signup (cannot be exported on the free plan)
  - `created_at` string, required
  - `clips_count` integer, required

## Other responses

- `400`: Validation error — the message names the field, e.g. `champ subtitles.size invalide : attendu 1..3`
- `402`: Plan quota reached (monthly videos, daily searches, file size/duration on free)

---

[API](https://skmtc.dev/cliponit/apis/cliponit-api.md) · [All operations](https://skmtc.dev/cliponit/apis/cliponit-api/llms.txt) · [OpenAPI document](https://skmtc.dev/cliponit/apis/cliponit-api/revisions/10e3e4327922?raw)
