> 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.

# Make any-to-video search requests

POST https://api.twelvelabs.io/v1.3/search
Content-Type: multipart/form-data

Use this endpoint to search for relevant matches in an index using text, media, or a combination of both as your query.

**Text queries**:

* Use the `query_text` parameter to specify your query.

**Media queries**:

* Set the `query_media_type` parameter to the corresponding media type (example: `image`).
* Provide up to 10 images by specifying the following parameters multiple times:
  * `query_media_url`: Publicly accessible URL of your media file.
  * `query_media_file`: Local media file.
    **Composed text and media queries**:
* Use the `query_text` parameter for your text query.
* Set `query_media_type` to `image`.
* Provide up to 10 images by specifying the `query_media_url` and `query_media_file` parameters multiple times.

**Entity search** (beta):

* To find a specific person in your videos, enclose the unique identifier of the entity you want to find in the `query_text` parameter.

- When using images in your search queries (either as media queries or in composed searches), ensure your images meet the [requirements](/v1.3/docs/concepts/models/marengo/marengo-3-0#image-file-requirements).
- This endpoint is rate-limited. For details, see the [Rate limits](/v1.3/docs/get-started/rate-limits) page.

Reference: https://docs.twelvelabs.io/api-reference/any-to-video-search/make-search-request

## Authentication

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

## Request

### Body (multipart/form-data)

This endpoint expects a multipart form.

- `query_media_type` (enum, optional) — The type of media you wish to use. This parameter is required for media queries. For example, to perform an image-based search, set this parameter to `image`. Use `query_text` together with this parameter when you want to perform a composed image+text search.
- `query_media_url` (SearchPostRequestBodyContentMultipartFormDataSchemaQueryMediaUrl, optional) — The publicly accessible URL of a media file to use as a query. This parameter is required for media queries if `query_media_file` is not provided. You can provide up to 10 images by specifying this parameter multiple times: ``` --form query_media_url=https://example.com/image1.jpg \ --form query_media_url=https://example.com/image2.jpg ```
- `query_media_file` (SearchPostRequestBodyContentMultipartFormDataSchemaQueryMediaFile, optional) — A local media file to use as a query. This parameter is required for media queries if `query_media_url` is not provided. You can provide up to 10 images by specifying this parameter multiple times: ``` --form query_media_file=@/path/to/image1.jpg \ --form query_media_file=@/path/to/image2.jpg ```
- `query_text` (string, optional) — The text query to search for. This parameter is required for text queries. Note that the platform supports full natural language-based search. You can use this parameter together with `query_media_type` and `query_media_url` or `query_media_file` to perform a composed image+text search. If you're using the Entity Search feature to search for specific persons in your video content, you must enclose the unique identifier of your entity between the `<@` and `>` markers. For example, to search for an entity with the ID `entity123`, use `<@entity123> is walking` as your query. Marengo supports up to 500 tokens per query.
- `index_id` (string, required) — The unique identifier of the index to search.
- `search_options` (list of enum, required) — Specifies the modalities the video understanding model uses to find relevant information. Available options: * `visual`: Searches visual content. * `audio`: Searches non-speech audio. * `transcription`: Spoken words - You can specify multiple search options in conjunction with the [`operator`](/v1.3/api-reference/any-to-video-search/make-search-request#request.body.operator.operator) parameter described below to broaden or narrow your search. For example, to search using both visual and non-speech audio content, include this parameter two times in the request as shown below: ```JSON --form search_options=visual \ --form search_options=audio \ --form search_options=transcription \ ``` For guidance, see the [Search options](/v1.3/docs/concepts/modalities#search-options) section.
- `transcription_options` (list of enum, optional) — Specifies how the platform matches your text query with the words spoken in the video. This parameter applies only when the `search_options` parameter contains the `transcription` value. Available options: - `lexical`: Exact word matching - `semantic`: Meaning-based matching For details on when to use each option, see the [Transcription options](/v1.3/docs/concepts/modalities#transcription-options) section. **Default**: `["lexical", "semantic"]`.
- `group_by` (enum, optional) — Use this parameter to group or ungroup items in a response. It can take one of the following values: - `video`: The platform will group the matching video clips in the response by video. - `clip`: The matching video clips in the response will not be grouped. **Default:** `clip`
- `operator` (enum, optional) — Combines multiple search options using `or` or `and`. Use `and` to find segments matching all search options. Use `or` to find segments matching any search option. For detailed guidance on using this parameter, see the [Combine multiple modalities](/v1.3/docs/concepts/modalities#combine-multiple-modalities) section. **Default**: `or`.
- `page_limit` (integer, optional) — The number of items to return on each page. When grouping by video, this parameter represents the number of videos per page. Otherwise, it represents the maximum number of video clips per page. **Max**: `50`.
- `filter` (string, optional) — Specifies a stringified JSON object to filter your search results. Supports both system-generated metadata (example: video ID, duration) and user-defined metadata. **Syntax for filtering** The following table describes the supported data types, operators, and filter syntax: | Data type | Operator | Description | Syntax | | :----------------------- | :---------------- | :------------------------------------------------------------------------------ | :------------------------------------------------------------------- | | String | `=` | Matches results equal to the specified value. | `{"field": "value"}` | | Array of strings | `=` | Matches results with any value in the specified array. Supported only for `id`. | `{"id": ["value1", "value2"]}` | | Numeric (integer, float) | `=`, `lte`, `gte` | Matches results equal to or within a range of the specified value. | `{"field": number}` or `{"field": { "gte": number, "lte": number }}` | | Boolean | `=` | Matches results equal to the specified boolean value. | `{"field": true}` or `{"field": false}`. | **System-generated metadata** The table below describes the system-generated metadata available for filtering your search results: | Field name | Description | Type | Example | | :--------- | :----------------------------------------------------------------------------------------- | :------------------------------------- | :------------------------------------------------------------------- | | `id` | Filters by specific video IDs. | Array of strings | `{"id": ["67cec9caf45d9b64a58340fc", "67cec9baf45d9b64a58340fa"]}`. | | `duration` | Filters based on the duration of the video containing the segment that matches your query. | Number or object with `gte` and `lte` | `{"duration": 600}` or `{"duration": { "gte": 600, "lte": 800 }}` | | `width` | Filters by video width (in pixels). | Number or object with `gte` and `lte` | `{"width": 1920}` or `{"width": { "gte": 1280, "lte": 1920}}` | | `height` | Filters by video height (in pixels). | Number or object with `gte` and `lte`. | `{"height": 1080}` or `{"height": { "gte": 720, "lte": 1080 }}`. | | `size` | Filters by video size (in bytes) | Number or object with `gte` and `lte`. | `{"size": 1048576}` or `{"size": { "gte": 1048576, "lte": 5242880}}` | | `filename` | Filters by the exact file name. | String | `{"filename": "Animal Encounters part 1"}` | **User-defined metadata** To filter by user-defined metadata: 1. Add metadata to your video by calling the [`PUT`](/v1.3/api-reference/videos/update) method of the `/indexes/:index-id/videos/:video-id` endpoint 2. Reference the custom field in your filter object. For example, to filter videos where a custom field named `needsReview` of type boolean is `true`, use `{"needs_review": true}`. For more details and examples, see the [Filter search results](/v1.3/docs/guides/search/filtering) page.
- `include_user_metadata` (boolean, optional) — Specifies whether to include user-defined metadata in the search results.

## Response

### 200

Successfully performed a search request.

- `data` (list of SearchItem, optional) — An array that contains your search results. For each match found, the model returns the following fields:
- `page_info` (SearchResultsPageInfo, optional) — An object that provides information about pagination.
- `search_pool` (search_pool, optional) — An object that contains details about the index you queried.

## 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.

### 429 Too Many Requests Error

If the rate limit is reached, the platform returns an `HTTP 429 - Too Many Requests` error response. The response body is empty.

- `any`

## Types

### SearchItem

An object that contains the search results.

- `start` (double, optional) — The start time of the matching video clip, expressed in seconds.
- `end` (double, optional) — The end time of the matching video clip, expressed in seconds.
- `video_id` (string, optional) — A string representing the unique identifier of the video. Once the platform indexes a video, it assigns a unique identifier. Note that this is different from the identifier of the video indexing task.
- `rank` (integer, optional) — The relevance ranking assigned by the model. Lower numbers indicate higher relevance, starting with 1 for the most relevant result.
- `thumbnail_url` (string, optional) — If thumbnail generation has been enabled for this index, the platform returns a string representing the URL of the thumbnail. Note that the URL expires in one hour.
- `transcription` (string, optional) — A transcription of the spoken words that are captured in the video.
- `id` (string, optional) — A string representing the unique identifier of the video. It only appears when the `group_by=video` parameter is used in the request.
- `user_metadata` (map from string to UserMetadataValue, optional) — Metadata that helps you categorize your assets. The object contains user-defined keys and values, where keys are strings. Each value is a string, a number, a boolean, or an array of strings. Send an integer wider than 53 bits (-9007199254740991 to 9007199254740991), and any identifier you want preserved verbatim, as a string. **Example**: ```JSON "user_metadata": { "category": "recentlyAdded", "batchNumber": 5, "rating": 9.3, "needsReview": true, "hashtags": ["summer", "vlog"] } ```
- `clips` (list of SearchItemClipsItems, optional) — An array that contains detailed information about the clips that match your query. The platform returns this array only when the `group_by` parameter is set to `video` in the request.

### SearchResultsPageInfo

An object that provides information about pagination.

- `limit_per_page` (integer, optional) — The maximum number of items on each page. When grouping by video, this field represents the maximum number of videos per page. Otherwise, it represents the maximum number of video clips per page.
- `page_expires_at` (string, optional) — A string representing the date and time, in the RFC 3339 format ("YYYY-MM-DDTHH:mm:ssZ"), that the page expires.
- `total_results` (integer, optional) — The total number of results. When grouping by video, this field represents the total number of video clips matching your query. Otherwise , this field represents the total number of videos.
- `total_inner_matches` (integer, optional) — When grouping by video, the platform return this field that shows the total number of video clips matching your query.
- `next_page_token` (string, optional) — The unique identifier of the next page.

### search_pool

An object that contains details about the index you queried.

- `total_count` (integer, optional) — The number of videos in the index you queried.
- `total_duration` (double, optional) — The total duration of the videos.
- `index_id` (string, optional) — The unique identifier of the index.

### 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.

### SearchItemClipsItems

- `start` (double, optional) — The start time of the matching video clip, expressed in seconds.
- `end` (double, optional) — The end time of the matching video clip, expressed in seconds.
- `rank` (integer, optional) — The relevance ranking assigned by the model. Lower numbers indicate higher relevance, starting with 1 for the most relevant result.
- `thumbnail_url` (string, optional) — If thumbnail generation has been enabled for this index, the platform returns a string representing the URL of the thumbnail. Note that the URL expires in one hour.
- `transcription` (string, optional) — A transcription of the spoken words that are captured in the clip.
- `video_id` (string, optional) — A string representing the unique identifier of the video for the corresponding clip.
- `user_metadata` (map from string to UserMetadataValue, optional) — Metadata that helps you categorize your assets. The object contains user-defined keys and values, where keys are strings. Each value is a string, a number, a boolean, or an array of strings. Send an integer wider than 53 bits (-9007199254740991 to 9007199254740991), and any identifier you want preserved verbatim, as a string. **Example**: ```JSON "user_metadata": { "category": "recentlyAdded", "batchNumber": 5, "rating": 9.3, "needsReview": true, "hashtags": ["summer", "vlog"] } ```

## Examples

**Request**

```json
{
  "query_text": "A man walking a dog",
  "index_id": "6298d673f1090f1100476d4c",
  "search_options": [
    "visual"
  ],
  "group_by": "clip",
  "operator": "or",
  "page_limit": 10,
  "filter": "{\"id\":[\"66284191ea717fa66a274832\"]}"
}
```

**Response**

```json
{
  "data": [
    {
      "start": 238.75,
      "end": 259.62109375,
      "video_id": "639963a1ce36463e0199c8c7",
      "rank": 1,
      "thumbnail_url": "https://example.com/thumbnail.jpg",
      "transcription": "A woman vlogs about her summer day, sharing her experience"
    }
  ],
  "search_pool": {
    "total_count": 10,
    "total_duration": 8731,
    "index_id": "639961c9e219c90227c371a2"
  }
}
```

**SDK Code**

```python search_create_example
import requests

url = "https://api.twelvelabs.io/v1.3/search"

payload = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"query_media_type\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"query_media_url\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"query_media_file\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"query_text\"\r\n\r\nA man walking a dog\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"index_id\"\r\n\r\n6298d673f1090f1100476d4c\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"transcription_options\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"group_by\"\r\n\r\nclip\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"operator\"\r\n\r\nor\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"page_limit\"\r\n\r\n10\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"filter\"\r\n\r\n{\"id\":[\"66284191ea717fa66a274832\"]}\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"include_user_metadata\"\r\n\r\n\r\n-----011000010111000001101001--\r\n"
headers = {
    "x-api-key": "<apiKey>",
    "Content-Type": "multipart/form-data; boundary=---011000010111000001101001"
}

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

print(response.json())
```

```javascript search_create_example
const url = 'https://api.twelvelabs.io/v1.3/search';
const form = new FormData();
form.append('query_media_type', '');
form.append('query_media_url', '');
form.append('query_media_file', '');
form.append('query_text', 'A man walking a dog');
form.append('index_id', '6298d673f1090f1100476d4c');
form.append('transcription_options', '');
form.append('group_by', 'clip');
form.append('operator', 'or');
form.append('page_limit', '10');
form.append('filter', '{"id":["66284191ea717fa66a274832"]}');
form.append('include_user_metadata', '');

const options = {method: 'POST', headers: {'x-api-key': '<apiKey>'}};

options.body = form;

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

```go search_create_example
package main

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

func main() {

	url := "https://api.twelvelabs.io/v1.3/search"

	payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"query_media_type\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"query_media_url\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"query_media_file\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"query_text\"\r\n\r\nA man walking a dog\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"index_id\"\r\n\r\n6298d673f1090f1100476d4c\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"transcription_options\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"group_by\"\r\n\r\nclip\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"operator\"\r\n\r\nor\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"page_limit\"\r\n\r\n10\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"filter\"\r\n\r\n{\"id\":[\"66284191ea717fa66a274832\"]}\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"include_user_metadata\"\r\n\r\n\r\n-----011000010111000001101001--\r\n")

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

	req.Header.Add("x-api-key", "<apiKey>")

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

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

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

}
```

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

url = URI("https://api.twelvelabs.io/v1.3/search")

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

request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<apiKey>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"query_media_type\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"query_media_url\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"query_media_file\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"query_text\"\r\n\r\nA man walking a dog\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"index_id\"\r\n\r\n6298d673f1090f1100476d4c\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"transcription_options\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"group_by\"\r\n\r\nclip\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"operator\"\r\n\r\nor\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"page_limit\"\r\n\r\n10\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"filter\"\r\n\r\n{\"id\":[\"66284191ea717fa66a274832\"]}\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"include_user_metadata\"\r\n\r\n\r\n-----011000010111000001101001--\r\n"

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

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

HttpResponse<String> response = Unirest.post("https://api.twelvelabs.io/v1.3/search")
  .header("x-api-key", "<apiKey>")
  .body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"query_media_type\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"query_media_url\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"query_media_file\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"query_text\"\r\n\r\nA man walking a dog\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"index_id\"\r\n\r\n6298d673f1090f1100476d4c\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"transcription_options\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"group_by\"\r\n\r\nclip\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"operator\"\r\n\r\nor\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"page_limit\"\r\n\r\n10\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"filter\"\r\n\r\n{\"id\":[\"66284191ea717fa66a274832\"]}\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"include_user_metadata\"\r\n\r\n\r\n-----011000010111000001101001--\r\n")
  .asString();
```

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

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.twelvelabs.io/v1.3/search', [
  'multipart' => [
    [
        'name' => 'query_text',
        'contents' => 'A man walking a dog'
    ],
    [
        'name' => 'index_id',
        'contents' => '6298d673f1090f1100476d4c'
    ],
    [
        'name' => 'group_by',
        'contents' => 'clip'
    ],
    [
        'name' => 'operator',
        'contents' => 'or'
    ],
    [
        'name' => 'page_limit',
        'contents' => '10'
    ],
    [
        'name' => 'filter',
        'contents' => '{"id":["66284191ea717fa66a274832"]}'
    ]
  ]
  'headers' => [
    'x-api-key' => '<apiKey>',
  ],
]);

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

```csharp search_create_example
using RestSharp;

var client = new RestClient("https://api.twelvelabs.io/v1.3/search");
var request = new RestRequest(Method.POST);
request.AddHeader("x-api-key", "<apiKey>");
request.AddParameter("undefined", "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"query_media_type\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"query_media_url\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"query_media_file\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"query_text\"\r\n\r\nA man walking a dog\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"index_id\"\r\n\r\n6298d673f1090f1100476d4c\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"transcription_options\"\r\n\r\n\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"group_by\"\r\n\r\nclip\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"operator\"\r\n\r\nor\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"page_limit\"\r\n\r\n10\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"filter\"\r\n\r\n{\"id\":[\"66284191ea717fa66a274832\"]}\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"include_user_metadata\"\r\n\r\n\r\n-----011000010111000001101001--\r\n", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift search_create_example
import Foundation

let headers = ["x-api-key": "<apiKey>"]
let parameters = [
  [
    "name": "query_media_type",
    "value": 
  ],
  [
    "name": "query_media_url",
    "value": 
  ],
  [
    "name": "query_media_file",
    "value": 
  ],
  [
    "name": "query_text",
    "value": "A man walking a dog"
  ],
  [
    "name": "index_id",
    "value": "6298d673f1090f1100476d4c"
  ],
  [
    "name": "transcription_options",
    "value": 
  ],
  [
    "name": "group_by",
    "value": "clip"
  ],
  [
    "name": "operator",
    "value": "or"
  ],
  [
    "name": "page_limit",
    "value": "10"
  ],
  [
    "name": "filter",
    "value": "{\"id\":[\"66284191ea717fa66a274832\"]}"
  ],
  [
    "name": "include_user_metadata",
    "value": 
  ]
]

let boundary = "---011000010111000001101001"

var body = ""
var error: NSError? = nil
for param in parameters {
  let paramName = param["name"]!
  body += "--\(boundary)\r\n"
  body += "Content-Disposition:form-data; name=\"\(paramName)\""
  if let filename = param["fileName"] {
    let contentType = param["content-type"]!
    let fileContent = String(contentsOfFile: filename, encoding: String.Encoding.utf8)
    if (error != nil) {
      print(error as Any)
    }
    body += "; filename=\"\(filename)\"\r\n"
    body += "Content-Type: \(contentType)\r\n\r\n"
    body += fileContent
  } else if let paramValue = param["value"] {
    body += "\r\n\r\n\(paramValue)"
  }
}

let request = NSMutableURLRequest(url: NSURL(string: "https://api.twelvelabs.io/v1.3/search")! 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()
```