> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://uploadcare.com/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://uploadcare.com/docs/_mcp/server.

# Video processing

> Video processing optimizes your video content delivery by letting you adjust video quality, format, and dimensions.

Uploadcare can encode video files from all popular formats to MP4, WEBM, or
OGG, adjust their quality, dimensions, cut out a video fragment, and generate
thumbnails via [REST API](https://uploadcare.com/docs/api/rest/).

Additionally, once any video file is uploaded, we extract its metadata (duration, format,
bitrate, and specific audio & video stream metadata: codec, bitrate, frame rate,
and dimensions). It becomes available under the `content_info.video` key of the file
information object. The file information can be retrieved by requesting to the
[REST API (v0.7 or higher)](/docs/api/rest).

## How it works \[#workflow]

Video processing works asynchronously through the REST API,
unlike the [image processing operations](/docs/transformations/image/#image-transformations-list) performed on the fly.

It generates encoded output as a separate file while the original file remains intact.

1. Start a processing job via REST API. Send an input file UUID with
   necessary processing operations.
2. Wait until the processing job status becomes `finished`.
3. The processed file can be addressed via its new UUID, as well
   as an URL with operations provided in a processing request.

Every video operation produces at least 2 files
(even if it hasn't been requested explicitly):

* Transcoded video
* A thumbnail

### Processing job \[#process]

The endpoint for your requests is:

```
https://api.uploadcare.com/convert/video/
```

```bash
curl -X POST \
    -H "Content-Type: application/json" \
    -H "Accept: application/vnd.uploadcare-v0.7+json" \
    -H "Authorization: Uploadcare.Simple $YOUR_PUBLIC_KEY:$YOUR_SECRET_KEY" \
    -d '{"paths": ["$UUID/video/-/format/ogg/"], "store": "1"}' \
    "https://api.uploadcare.com/convert/video/"
```

Project pointed with `$YOUR_PUBLIC_KEY` and `$YOUR_SECRET_KEY` should have
[enabled](#billing) Video processing feature. The `$UUID` of the source
file to convert should belong to the project.

Input file identifiers should be formatted with UUID followed be `/video/`
(please note, this is not operation, so there shouldn't be `/-/` between them)
and ended by [operations](#process-operations) separated by `/-/`:

```cdn
/:uuid/video/-/:operation/:parameters/-/:operation/:parameters/
```

Operations can be omitted; the video will then be delivered as `mp4` with
its quality is set to `normal`.

**Check out [detailed API reference for `POST /convert/video/`](https://uploadcare.com/docs/api/rest/conversion/video-convert).**

### Webhook event

To get the job result, you need to enable `file.uploaded` in the
[Webhook section](/docs/webhooks/#webhook-notifications) of the dashboard.

After completing the processing job, the webhook will be sent to
the endpoint you specified in the webhook settings.

File information in response:

```json
  "initiator": {
      "type": "addon",
      "detail": {
          "addon_name": "zencoder_convert_video",
          "request_id": ":request_id",
          "source_file_uuid": ":UUID"
      }
   }
   "hook": {
    ...
    "event": "file.uploaded",
    "is_active": true,
    "version": "0.7",
    "created_at": "2023-10-26T09:32:40.114516Z",
    "updated_at": "2023-10-26T09:32:40.114516Z"
},
"data": {
    "content_info": {
        "video": {
            "duration": 5769,
            "format": "mpeg4",
            "bitrate": 1778,
            "audio": [{
                "bitrate": 87,
                "codec": "aac",
                "sample_rate": 44100,
                "channels": 2
            }],
            "video": [{
                "height": 248,
                "width": 440,
                "frame_rate": 30,
                "bitrate": 1691,
                "codec": "h264"
            }]
        }
    }
}
```

After receiving `source_file_uuid`, you can make a GET request for the
original file and get all the variations from there, including the thumbnail:

```json
"uuid": ":UUID", "variations": {
    "video/-/format/mp4/-/quality/normal/-/size/440x380/": ":UUID_1",
    "video/-/format/mp4/-/quality/normal/-/size/512x384/": ":UUID_2",
    "video/-/format/mp4/-/quality/normal/-/size/440x380/-/thumbs~1/0/": ":UUID_1_thumbs",
    "video/-/format/mp4/-/quality/normal/-/size/512x384/-/thumbs~1/0/": ":UUID_2_thumbs"
}
```

### Job status \[#status]

If your application does not have a backend or uses a mobile version,
you can submit the request yourself.

As a request result, you immediately receive an UUID of the new file. But it
can be not accessible due to that conversion takes time. Once you get a
processing job result, you can get a processing job status using `:token`.

```bash
curl -X GET \
    -H "Content-Type: application/json" \
    -H "Accept: application/vnd.uploadcare-v0.7+json" \
    -H "Authorization: Uploadcare.Simple $YOUR_PUBLIC_KEY:$YOUR_SECRET_KEY" \
    "https://api.uploadcare.com/convert/video/status/:token/"
```

**Check out [detailed API reference for `GET /convert/video/status/{token}/`](https://uploadcare.com/docs/api/rest/conversion/video-convert-status).**

### Job result

The processed video becomes addressable via both a new UUID and a CDN URL
you provided in the request:

```cdn
https://:subdomain.ucarecd.net/:new_uuid/
https://:subdomain.ucarecd.net/:original_uuid/video/-/:operation/:parameters/
```

Applying the same set of conversion operations to the original video will create
a new file with a different `:new_uuid`, the conversion result will be the same.

## List of video operations \[#process-operations]

You can change your video canvas size, quality, format, and length by applying
video transform operations. This way, you can optimize video and find a perfect
balance between the desired video quality, loading time, and traffic usage.

### Size \[#operation-size]

`-/size/:one_or_two_dimensions/`\

`-/size/:one_or_two_dimensions/:resize_mode/`

Downscales a video to the specified dimensions.
If the video is smaller, it does nothing.

* `:one_or_two_dimensions`: width and height in pixels separated by `x`,
  one of the values can be omitted.\

  The value you specify for any dimensions should be a
  *non-zero integer divisible by 4*.\

  Examples: `720x540`, `720x`, `x540`.
* `:resize_mode`: presets that control aspect ratio and cropping behavior:
  * `preserve_ratio` (default): preserve the aspect ratio of the original
    file.
  * `change_ratio`: matches the output video to provided dimensions,
    no matter the original aspect ratio.
  * `scale_crop`: matches the output video to provided dimensions and
    crops the rest of the pixels along with one ax (top/bottom or left/right).
  * `add_padding`: letterbox the video to exactly match the output frame
    size (add black bars).

If you explicitly specify a non-default `:resize_mode`, the missing dimension
size will be taken from the original file. For instance,
`-/size/x600/change_ratio/` for a 640x480 original frame size will output
your video as `640x600`.

Here are some examples:

```cdn
/:uuid/video/-/size/720x540/
/:uuid/video/-/size/800x600/change_ratio/
/:uuid/video/-/size/512x384/scale_crop/
/:uuid/video/-/size/512x384/add_padding/
/:uuid/video/-/size/720x/
/:uuid/video/-/size/x540/
```

### Resolution limits \[#resolution-limits]

When processing videos, the output resolution cannot exceed **4K (4096×2160)** by more than **10%**.\
If the output resolution is higher, the job will fail with the following error:

```
Output file error. InvalidVideoSettingsError: The resolution cannot exceed 4k (4096x2160) by more than 10%.
```

You can check your source video's resolution using the [`file_info`](https://uploadcare.com/docs/api/upload/upload/file-upload-info) → `content_info.video` field:

```json
"content_info": {
  "video": {
    "width": 3840,
    "height": 2160,
    ...
  }
}
```

If the source video exceeds the allowed dimensions, apply the [`size`](https://uploadcare.com/docs/transformations/image/resize-crop#operation-resize) operation to scale it down:

```
https://{subdomain}.ucarecd.net/<UUID>/-/size/3840x2160/
```

This ensures the output video stays within the supported resolution range.

### Quality \[#operation-quality]

`-/quality/:value/`

Sets the video quality level that affects the file size. In turn, it affects
loading time and user-generated traffic.

* `:value`: one of the following values:
  * `normal` (default): suits most cases.
  * `better`: better video quality, larger file size compared to `normal`.
  * `best`: useful when you want perfect quality and don't care about the
    file size. It's larger than `better`.
  * `lighter`: saves traffic without a significant subjective quality loss,
    smaller file size compared to `normal`.
  * `lightest`: lowest visual quality that provides a minimal loading time,
    smaller than `lighter`.

### Format \[#operation-format]

`-/format/:format/`

Converts a file to one of the web video formats.

* `:format`: one of the following values:
  * `mp4` (default): MPEG-4 is widely supported across devices and
    [browsers mp4](https://caniuse.com#feat=mpeg4). Videos encoded with `mp4` are compressed
    via H.264 codec and will work on Android and iOS, in Safari, Chrome,
    and EDGE. Choose it when you want to go universal or in case you need
    a fallback.
  * `webm`: WebM is an open media file format designed for the web.
    Video streams are compressed via VP8 or VP9 video codec.
    Audio gets compressed with Vorbis or Opus, [more WebM](https://www.webmproject.org/about/faq/).
    is compatible with many of the current [devices](https://developer.android.com/guide/topics/media/media-formats.html)
    and [browsers WebM](https://caniuse.com#feat=webm) and backed by Google.
  * `ogg`: Ogg/Theora is a free and open video compression format from Xiph.org.
    [Theora](https://www.theora.org/) is considered competitive at low bitrates, making it suitable for
    the web.
    Theora is backed by the community and supported by
    [fewer browsers](https://caniuse.com#feat=ogv) than `webm`.

### Cut \[#operation-cut]

`-/cut/:start_time/:length/`

Cuts out a video fragment based on the following parameters:

* `:start_time`: defines the starting point of a fragment to cut based
  on your input file timeline.
* `:length`: defines the duration of that fragment.

Both of the parameters can be formatted in one of the following ways:

* `HHH:MM:SS.sss`: consists of the following parameters:
  * `HHH`: hours ranging from 0 to 999, can be omitted.
  * `MM`: minutes ranging from 0 to 59, can be omitted.
  * `SS`: second ranging from 0 to 59.
  * `sss`: fractional part of a second ranging from 0 to 999,
    сan be omitted.
* `SSSS.sss`: consists of the following parameters:
  * `SSSS`: second ranging from 0 to 9999999.
  * `sss`: fractional part of a second ranging from 0 to 999,
    сan be omitted.

You can also set `:length` to `end` keyword and your video fragment will
include all the duration of your input starting at `:start_time`.

Note that `:start_time` and `:length` may not be exact depending on how
keyframes are arranged within your input video.

Here are some examples:

```cdn
/:uuid/video/-/cut/1:2:40.535/2:20.0/
/:uuid/video/-/cut/001:02:40.535/02:20.0/
/:uuid/video/-/cut/2:30.535/2:20.0/
/:uuid/video/-/cut/1:2:40.535/end/
/:uuid/video/-/cut/3760.535/140/
/:uuid/video/-/cut/3760.535/end/
```

## Video thumbnails \[#operation-thumbs]

Note, `thumbs` should be put last in your sequence of operations in a processing job URL.

> **Info**
>
> To get a single still image from an exact position in a video without starting a
> processing job, use [videoframe](/docs/transformations/videoframe/) instead.

### Generating thumbnails

`-/thumbs~N/`

When specified in the [processing request](#process), it'll create `N`
thumbnails for your video, where `N` is a non-zero integer ranging from 1 to 50;
defaults to 1.

If the operation is omitted, a single thumbnail gets generated from the middle
frame of your video. If you define another `N`, thumbnails are generated from
the frames evenly distributed along your video timeline. For example, if you
have a 20-second video with `N` set to 20, it'll generate a thumbnail every
second of your video.

Whenever thumbnails should be generated from the beginning of the video, it is
set by an extra argument to `thumbs~N` operation:

* `yes` for generating from the start of the video.
* `no` (default): from the middle.

`cdn›-/thumbs~1/yes/`: will generate a single thumbnail from first frame.

### Requesting thumbnails

In an API response, you get a `thumbnails_group_uuid`. It is a collection of
image files stored as a [file group](/docs/file-groups/). You can access
individual images and apply [image processing operations](/docs/transformations/image/#image-transformations-list).
There also is a way to request thumbnails.

```cdn
/:uuid/video/-/:operations/-/thumbs~20/:number/
```

Where `:uuid` and `:operations` are your video file identifier and processing
operations passed in a [processing job](#process) request.

* `:number` is a zero-based index of a particular thumbnail in a created set.
  In the example above, the `:number` ranges from 0 to 19.

You can also go with:

```cdn
/:uuid/video/-/:operations/-/thumb/
```

Which is a shortcut for:

```cdn
/:uuid/video/-/:operations/-/thumbs~1/0/
```

If you omit a thumbnail index, we return the first thumbnail from your set.
In the following example, the first thumbnail from 20 is returned:

```cdn
/:uuid/video/-/:operations/-/thumbs~20/
```

## API integrations \[#integrations]

You don't have to code most of the low-level API integrations.
We have high-level libraries for all popular platforms:

* [JavaScript](/docs/integrations/javascript/)
* [PHP](/docs/integrations/php/)
* [Python & Django](/docs/integrations/python/)
* [Ruby](/docs/integrations/ruby/)
* [Rails](/docs/integrations/rails/)
* [Golang](/docs/integrations/golang/)
* [Rust](/docs/integrations/rust/)
* [Swift](/docs/integrations/swift/)
* [Android](/docs/integrations/android/)

## Billing

* This feature is available on [paid plans](https://uploadcare.com/pricing/).
* Learn how we charge for [Video processing operations](/docs/billing#video-processing).

### Examples

* Converting a 5-minute HD video to an SD MP4 will be billed as 5 minutes.
* Converting a 5-minute HD video to an HD MP4 will be billed as 10 minutes.
* Converting a 5-minute UHD video to a UHD MP4 will be billed as 20 minutes.
* Cutting the first minute from a 5-minute HD video using the `cut` operation while preserving the
  frame size, will be billed as 2 minutes.
* Generating 5 thumbnails from a 5-minute SD video will be billed as 5 minutes
  (even if the conversion request doesn't contain any operations other besides `thumbs`,
  the whole video will be transcoded to the default format `MP4`
  and the default quality `normal`).