> This page is for version v1.3 (default).
> For other versions, use one of these documentation indexes:
> - v1.3 (default): https://docs.twelvelabs.io/v1.3/llms.txt

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

# Create a multipart upload session

POST https://api.twelvelabs.io/v1.3/assets/multipart-uploads
Content-Type: application/json

This method creates a multipart upload session for a local file.

**Supported content**: Video, audio, and images.

**Upload limits**:
- **Video and audio**: Up to 10 GB
- **Images**: Up to 32 MB

**Additional requirements** depend on your workflow:
- **Search**: [Marengo requirements](/v1.3/docs/concepts/models/marengo/marengo-3-0#video-file-requirements)
- **Video analysis**: [Pegasus requirements](/v1.3/docs/concepts/models/pegasus/pegasus-1-6#input-requirements)
- **Entity search**: [Marengo image requirements](/v1.3/docs/concepts/models/marengo/marengo-3-0#image-file-requirements)
- **Create embeddings**: [Marengo requirements](/v1.3/docs/concepts/models/marengo/marengo-3-5#input-requirements)


Reference: https://docs.twelvelabs.io/api-reference/upload-files/multipart-uploads/create

## Authentication

- `x-api-key` header (required) — Your API key. You can find your API key on the API Keys page.

## Request

### Body (application/json)

This endpoint expects a CreateAssetUploadRequest.

- `filename` (string, required) — The original file name of the asset.
- `type` (enum, required) — The type of asset you want to upload.
  - Allowed values: `video`, `audio`, `image`
- `total_size` (integer, required) — The total size of the file in bytes. The platform uses this value to: - Calculate the optimal chunk size. - Determine the total number of chunks required - Generate the initial set of presigned URLs **Upload limits**: - **Video and audio**: Up to 10 GB - **Images**: Up to 32 MB
- `enable_hls` (boolean, optional, default: false) — When set to `true`, the platform generates an HLS playlist and segments for streaming. Applicable to video and audio assets only. The platform ignores this flag for other asset types. **Default**: `false`.
- `enable_thumbnail` (boolean, optional, default: false) — When set to `true`, the platform generates thumbnail images from the uploaded content. **Default**: `false`.
- `user_metadata` (map from string to UserMetadataValue, optional) — Metadata that helps you categorize your assets. You can specify a list of keys and values. Keys are strings, and values can be a string, a number, a boolean, or an array of strings. A key set to an empty string (`""`), an empty array (`[]`), or `null` is omitted. Send an integer wider than 53 bits (-9007199254740991 to 9007199254740991), and any identifier you want preserved verbatim, as a string.

## Response

### 201

The multipart upload session has been successfully created.

- `upload_id` (string, optional) — The unique identifier of this upload session. Store this value, as you'll need it for the following subsequent operations: - Reporting completed chunks - Requesting additional presigned URLs - Retrieving the status of this upload session This identifier remains valid for 24 hours from the time of creation.
- `asset_id` (string, optional) — The unique identifier for the asset being created. Store this value, as you'll need it to reference the asset in other API calls. Note that this identifier is reserved immediately, but the asset becomes available for other operations only after the upload is completed successfully.
- `upload_urls` (list of PresignedURLChunk, optional) — An array containing the initial set of presigned URLs for uploading chunks. Each URL corresponds to a specific chunk. Note the following about the presigned URLs: * URLs expire after one hour. * Depending on the size of the file, the initial set may not include URLs for all chunks. If you need more URLs, you can request additional ones using the [`POST`](/v1.3/api-reference/upload-content/multipart-uploads/get-additional-presigned-urls) method of the `/assets/multipart-uploads/{upload_id}/presigned-urls` endpoint.
- `upload_headers` (map from string to string, optional)
- `chunk_size` (integer, optional) — The size in bytes for each chunk, except for the last chunk, which may be smaller. Use this value to divide your file into chunks of this exact size.
- `total_chunks` (integer, optional) — The total number of chunks into which your file must be split. Calculated as: ceiling(`total_size` / `chunk_size`).
- `expires_at` (string, optional) — A string representing the date and time, in RFC 3339 format (“YYYY-MM-DDTHH:mm:ssZ”), when the upload URL will expire. Upload URLs expire 24 hours from their creation. After expiration, you cannot resume the upload, and you must create a new upload session.

## Errors

### 400 Bad Request Error

The request has failed.

- `code` (string, optional) — A string representing the code associated with the error. See the [Error codes](/v1.3/api-reference/error-codes) page for details.
- `message` (string, optional) — A human-readable string describing the error, intended to be suitable for display in a user interface.

### 403 Forbidden Error

The request has failed.

- `code` (string, optional) — A string representing the code associated with the error. See the [Error codes](/v1.3/api-reference/error-codes) page for details.
- `message` (string, optional) — A human-readable string describing the error, intended to be suitable for display in a user interface.

### 500 Internal Server Error

The request has failed.

- `code` (string, optional) — A string representing the code associated with the error. See the [Error codes](/v1.3/api-reference/error-codes) page for details.
- `message` (string, optional) — A human-readable string describing the error, intended to be suitable for display in a user interface.

## Types

### UserMetadataValue

A single metadata value: a string, a number, a boolean, or an array of strings. The platform stores the value with the type you send. It rejects a nested object and an array that contains anything but strings. Send an integer wider than 53 bits (-9007199254740991 to 9007199254740991) and any identifier you want preserved verbatim as a string.

### PresignedURLChunk

- `chunk_index` (integer, optional) — The index of this chunk.
- `url` (string, optional) — The presigned URL for uploading this chunk. Each URL can only be used once and expires after 1 hour.
- `expires_at` (string, optional) — A string representing the date and time, in RFC 3339 format ("YYYY-MM-DDTHH:mm:ssZ"), when the resource will expire.

## Examples

**Request**

```json
{
  "filename": "my-video.mp4",
  "type": "video",
  "total_size": 104857600
}
```

**Response**

```json
{
  "upload_id": "507f1f77bcf86cd799439011",
  "asset_id": "507f1f77bcf86cd799439012",
  "upload_urls": [
    {
      "chunk_index": 1,
      "url": "https://s3.amazonaws.com/bucket/key?signature=...",
      "expires_at": "2025-12-01T18:00:00Z"
    }
  ],
  "upload_headers": {},
  "chunk_size": 5242880,
  "total_chunks": 20,
  "expires_at": "2025-12-01T12:00:00Z"
}
```

**SDK Code**

```python
import requests

url = "https://api.twelvelabs.io/v1.3/assets/multipart-uploads"

payload = {
    "filename": "my-video.mp4",
    "type": "video",
    "total_size": 104857600
}
headers = {
    "x-api-key": "<apiKey>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.twelvelabs.io/v1.3/assets/multipart-uploads';
const options = {
  method: 'POST',
  headers: {'x-api-key': '<apiKey>', 'Content-Type': 'application/json'},
  body: '{"filename":"my-video.mp4","type":"video","total_size":104857600}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.twelvelabs.io/v1.3/assets/multipart-uploads"

	payload := strings.NewReader("{\n  \"filename\": \"my-video.mp4\",\n  \"type\": \"video\",\n  \"total_size\": 104857600\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("x-api-key", "<apiKey>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.twelvelabs.io/v1.3/assets/multipart-uploads")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"filename\": \"my-video.mp4\",\n  \"type\": \"video\",\n  \"total_size\": 104857600\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.twelvelabs.io/v1.3/assets/multipart-uploads")
  .header("x-api-key", "<apiKey>")
  .header("Content-Type", "application/json")
  .body("{\n  \"filename\": \"my-video.mp4\",\n  \"type\": \"video\",\n  \"total_size\": 104857600\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.twelvelabs.io/v1.3/assets/multipart-uploads', [
  'body' => '{
  "filename": "my-video.mp4",
  "type": "video",
  "total_size": 104857600
}',
  'headers' => [
    'Content-Type' => 'application/json',
    'x-api-key' => '<apiKey>',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.twelvelabs.io/v1.3/assets/multipart-uploads");
var request = new RestRequest(Method.POST);
request.AddHeader("x-api-key", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"filename\": \"my-video.mp4\",\n  \"type\": \"video\",\n  \"total_size\": 104857600\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "x-api-key": "<apiKey>",
  "Content-Type": "application/json"
]
let parameters = [
  "filename": "my-video.mp4",
  "type": "video",
  "total_size": 104857600
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.twelvelabs.io/v1.3/assets/multipart-uploads")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```