Overview

On a high level, these are the steps you need to follow to upload a file via the API:

  1. Check the quota: before you can upload you need to make sure the file is not too big
  2. Get a ticket: all upload-related calls need a ticket
  3. Send the video data: there are multiple ways to do this (more on this later)
  4. Verify the transfer: make sure all your data made it to our servers
  5. Complete the upload: tell Vimeo that the data transfer is over and it's time to encode the video

We'll get into specifics below on exactly which calls are involved for each step, but before we begin there's one more thing you need to know. There are two ways to transfer the video data to us: either by POSTing the file or streaming the file.

Which approach you choose depends on the capabilities of the environment you're working in and your level of comfort with network programming. If you are working on a totally web-browser based uploading interface we you will have to use POST approach. If you are working on a more integrated application (such as a mobile app) or need robust resumable uploading, we recommend streaming. Both flows are described in detail below.

Unless otherwise noted all these API calls are OAuth requests with standard response formats. To get specifics (i.e. response parameters, error codes) on any of the methods in red below, please check out the full documentation.

Uploading via POST walkthrough

POST is the standard way for web browsers to transfer large files. If you're working in an environment that has robust built-in support for multipart POSTs (such as an Adobe AIR app), this is a good solution for you. If you want to provide a way for users to upload directly to Vimeo via a web page this is the only way to do it.

Check the user's quota

Before you begin call vimeo.videos.upload.getQuota to make sure the user has enough space available in their account and to let them know if their video will be encoded in HD.

Get an upload ticket

Before you can upload a video you'll need an upload ticket. You can get one by calling vimeo.videos.upload.getTicket. You should receive a response with a ticket id and an endpoint. Here is an example response in JSON (you can specify the response format):

{
 
"id":"abcdef124567890",
 
"endpoint":"http:\/\/1.2.3.4\/upload_multi?ticket_id=abcdef124567890",
 
"max_file_size":"524288000"
}

Transfer the video data

If more than a couple hours have passed since your ticket was generated, make sure to call vimeo.videos.upload.checkTicket to make sure that your ticket is still valid.

Now comes the tricky part. We're going to perform a POST to the endpoint returned in the vimeo.videos.upload.getTicket call.

The simplest way to illustrate this is with an HTML <form>. Here's a hypothetical <form> that uses the id and endpoint above.

<form method="POST" action="http://1.2.3.4/upload_multi?ticket_id=abcdef124567890" enctype="multipart/form-data">
    <input type="hidden" name="ticket_id" value="abcdef124567890" />
    <input type="hidden" name="chunk_id" value="0" />
    <input type="file" name="file_data" />
    <input type="submit" value="Upload" />
</form>

A form like this will produce a request to the upload server that looks something like this:

POST http://1.2.3.4/upload_multi?ticket_id=abcdef124567890 HTTP/1.1
Host: 1.2.3.4
Referer: http://mywebsite.com/upload
Content-Length: 339108
Cache-Control: max-age=0
Origin: http://vimeo.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryFlF3dFBPBvsDeXMZ

------WebKitFormBoundaryFlF3dFBPBvsDeXMZ
Content-Disposition: form-data; name="ticket_id"

abcdef124567890
------WebKitFormBoundaryFlF3dFBPBvsDeXMZ
Content-Disposition: form-data; name="chunk_id"

0
------WebKitFormBoundaryFlF3dFBPBvsDeXMZ
Content-Disposition: form-data; name="file_data"; filename="example_video.mp4"
Content-Type: video/mp4

.... binary data of your file here ....

Clearly this is a very verbose and quirky format, so unless you're working in a browser or a language that has robust POST support, we recommend trying the streaming approach to uploading (see below).

You may have noticed the chunk_id parameter. Vimeo's upload system allow multiple chunks of a file to be uploaded via POST and reassembled on the server. This is useful for resuming uploads or splitting up a file and uploading many parts in parallel. chunk_ids should be assigned in the order you want them combined, starting with 0.

Verify the upload

Call vimeo.videos.upload.verifyChunks to get a list of the chunks uploaded and their associated filesizes. You can use this to verify that everything arrived as it should have. If you find that the uploaded file's size is wrong, you can replace it by running the POST again with the same chunk_id. Or if an upload was accidentally terminated, you could check where the upload left off and POST the remainder of the file with an incremented chunk_id.

Here's an example XML response for an upload with 3 chunks, the chunk IDs correspond to the chunk_id you assigned when POSTing the chunk in step 3.

<?xml version="1.0" encoding="utf-8"?>
<rsp stat="ok" generated_in="0.0028">
    <ticket id="abcdef124567890">
        <chunk id="0" size="678900" />
        <chunk id="1" size="678900" />
        <chunk id="2" size="4003" />
    </ticket>
</rsp>

Complete the upload

The final step is to call vimeo.videos.upload.complete to queue up the video for and transcoding. You'll receive back the video_id from this call, which you can then use in other calls (set the title, description, privacy, etc.). If you do not call this method, the video will not be processed.

<?xml version="1.0" encoding="utf-8"?>
<rsp stat="ok" generated_in="0.0028">
    <ticket id="abcdef124567890" video_id="12345" />
</rsp>

Need an example?

The upload() function in our PHP library has been updated to take advantage of the POST upload method. If you're using PHP, we highly recommend that you use the library.

Uploading via streaming walkthrough

Streaming is a more efficient way to transfer video data to our upload servers. Instead of generating a multipart POST, a standard HTTP connection is opened and the binary data of the file is simply written into the socket. This is often much more efficient on the client side because it requires no manipulation of the file data. However, it requires more network programming experience and the ability to read headers.

Check the user's quota

Before you begin call vimeo.videos.upload.getQuota to make sure the user has enough space available in their account and to let them know if their video will be encoded in HD.

Get an upload ticket

Before you can upload a video you'll need an upload ticket. You can get one by calling vimeo.videos.upload.getTicket with the upload_method parameter set to streaming. You should receive a response with a ticket id and an endpoint. Here is an example response in JSON (you can specify the response format):

{
 
"id":"abcdef124567890",
 
"endpoint":"http:\/\/1.2.3.4:8080\/upload?ticket_id=abcdef124567890",
 
"max_file_size":"524288000"
}

Transfer the video data

If more than a couple hours have passed since your ticket was generated, make sure to call vimeo.videos.upload.checkTicket to make sure that your ticket is still valid.

To stream an upload to Vimeo you must perform a PUT call to the endpoint with headers specifying the Content-Length and Content-Type. Content-Length is simply the size of your file. Other headers are OK, they're just ignored. Here's an example of the format of the HTTP request.

PUT http://1.2.3.4:8080/upload?ticket_id=abcdef124567890 HTTP/1.1
Host: 1.2.3.4:8080
Content-Length: 339108
Content-Type: video/mp4


.... binary data of your file here ....

Successful uploads with have a HTTP 200 status code. A 501 error means you did not preform a PUT or the request was malformed.

Verify the upload

To check how much of a file has transferred, perform the exact same PUT request without the file data and with the header "Content-Range: bytes */*". Here's an example request:

PUT http://1.2.3.4:8080/upload?ticket_id=abcdef124567890 HTTP/1.1
Content-Length: 0
Content-Range: bytes */*

If this file exists, this will return a response with a HTTP 308 status code and a Range header with the number of bytes on the server.

HTTP/1.1 308
Content-Length: 0
Range: bytes=0-1000

If you perform a request like this and find that the returned number is NOT the size of your uploaded file, then it's a good idea to resume where you left off. To resume, preform the same PUT you did in step 3 but with an additional header with the following string format: "Content-Range: (last byte on server + 1)-(last byte I will be sending)/(total filesize)". If you noticed in the example above, our uploaded file was 339108 total bytes in size but our "Content-Range: */*" verification call returned a value of 1000. To resume we would send the following headers with the binary data of our file starting at byte 1001:

PUT http://1.2.3.4:8080/upload?ticket_id=abcdef124567890 HTTP/1.1
Host: 1.2.3.4:8080
Content-Length: 338108
Content-Type: video/mp4
Content-Range: bytes 1001-339108/339108


.... binary data of your file here ....

If all goes well you will receive a 200 status code. A 400 code means the Content-Range header did not resume from the last byte on the server.

Complete the upload

The final step is to call vimeo.videos.upload.complete to queue up the video for and transcoding. You'll receive back the video_id from this call, which you can then use in other calls (set the title, description, privacy, etc.). If you do not call this method, the video will not be processed.

<?xml version="1.0" encoding="utf-8"?>
<rsp stat="ok" generated_in="0.0028">
    <ticket id="abcdef124567890" video_id="12345" />
</rsp>