> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flex.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Video input

> Send a video to a chat model with the `video_url` content part, the same way you send an image.

Models that accept video take it inside a chat message, next to your text, the same way vision models take an image. Pass a list of content parts instead of a plain string: one `text` part and one `video_url` part. The model watches the video and answers in text.

Not every model can do this. A model accepts video when `video` is in its `input_modalities` on `GET /v1/models` (see [model discovery](https://docs.flex.ai/inference-api/guides/model-discovery.md)). The first model that does is `MiniMax-M3.1-Flash`. A model that reads images does not necessarily read video, so check the list rather than assuming.

## Example

```bash cURL theme={null}
curl https://api.flex.ai/v1/chat/completions \
  -H "Authorization: Bearer $FLEXAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "MiniMax-M3.1-Flash",
    "messages": [{
      "role": "user",
      "content": [
        {"type": "text", "text": "What happens in this video?"},
        {"type": "video_url", "video_url": {"url": "https://example.com/clip.mp4"}}
      ]
    }]
  }'
```

```python Python theme={null}
import os
from openai import OpenAI
client = OpenAI(base_url="https://api.flex.ai/v1", api_key=os.environ["FLEXAI_API_KEY"])

response = client.chat.completions.create(
    model="MiniMax-M3.1-Flash",
    messages=[{
        "role": "user",
        "content": [
            {"type": "text", "text": "What happens in this video?"},
            {"type": "video_url", "video_url": {"url": "https://example.com/clip.mp4"}},
        ],
    }],
)
print(response.choices[0].message.content)
```

```typescript TypeScript theme={null}
import OpenAI from "openai";
const client = new OpenAI({
  baseURL: "https://api.flex.ai/v1",
  apiKey: process.env.FLEXAI_API_KEY,
});

const response = await client.chat.completions.create({
  model: "MiniMax-M3.1-Flash",
  messages: [{
    role: "user",
    content: [
      { type: "text", text: "What happens in this video?" },
      // The OpenAI SDK types don't list video_url yet, so cast the part.
      { type: "video_url", video_url: { url: "https://example.com/clip.mp4" } } as any,
    ],
  }],
});
console.log(response.choices[0].message.content);
```

## Video sources

The `video_url.url` field accepts either a public `http(s)` URL or a base64 data URL. Use a data URL when the video isn't already hosted somewhere. Any other scheme, such as `file://` or `ftp://`, is refused with a 400.

```python Python theme={null}
import base64
with open("clip.mp4", "rb") as f:
    b64 = base64.b64encode(f.read()).decode()

data_url = f"data:video/mp4;base64,{b64}"
# pass data_url as video_url.url in the request above
```

The gateway does not download your video itself: the model's serving engine fetches the URL or decodes the data URL. That is why a URL it can't reach comes back as an error rather than being retried for you (see [Errors](#errors)).

## Limits

These are the limits on `MiniMax-M3.1-Flash`. Other video models may differ, so treat this table as that model's, not the API's.

| Limit | Value |
| - | - |
| Videos per request | Set by the model's serving engine (1 on `MiniMax-M3.1-Flash` today) |
| Size of the downloaded file | 50 MB |
| Frame sampling | 1 frame per second of video |
| Time allowed to fetch the URL | 30 seconds |

The gateway does not count the videos in your request. The per-request limit is set by the model's serving engine, and a request with more videos than that comes back as the engine's 400.

A longer video is sampled into more frames and so uses more of the model's context window. One video can add up to roughly 195,000 prompt tokens, so a long clip leaves less room for your text and for the answer.

## Usage accounting

Video is billed as prompt tokens, the same way image tokens are. The `usage` block in the response includes the tokens the video was encoded into in `prompt_tokens`, and there is no separate per-video or per-second charge for video *input* (video *generation* is a different product, [priced per second of output](https://docs.flex.ai/inference-api/reference/billing.md)).

Before a request runs, the gateway reserves credit for the largest the video could cost, then settles to the real usage when the request finishes. A balance below that reservation is refused with a 402 before any GPU time is spent; see [Errors](https://docs.flex.ai/inference-api/reference/errors.md).

## Errors

* **Model doesn't accept video.** Send a `video_url` part to a model whose `input_modalities` doesn't list `video` and you get a 400 naming the model, before anything runs and without a charge. Pick a model from the discovery list that does list it.
* **Unsupported URL scheme.** Anything other than `http://`, `https://` or `data:` is a 400 on `messages[i].content[j].video_url`.
* **Unfetchable or undecodable video.** A video that times out, a response that isn't a video we can decode, or a video over the size limit comes back as a 400 that starts `The provided video_url could not be fetched or decoded`. A host that can't be reached at all (a DNS or connection failure) can come back as a plain 400 `Invalid request.` instead, the same as for an unreachable `image_url`. Either way, check that the URL is public, returns a video file, and is within the limits above. Genuine gateway failures on a video request still return 5xx, so treat these 400s as "fix the video".
* **Too many videos.** More videos than the model's engine allows per request (see [Limits](#limits)) is the engine's own 400, not a gateway message.

Each of these is documented with its exact response body in the [error reference](https://docs.flex.ai/inference-api/reference/errors.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.