curl --request POST \
--url https://mavi-backend.memories.ai/serve/api/v2/vu/chat/completions \
--header 'Authorization: <api-key>' \
--header 'Content-Type: application/json' \
--data '
{
"model": "gemini:gemini-2.5-flash",
"messages": [
{
"content": "<string>"
}
],
"temperature": 0.7,
"max_tokens": 1000,
"top_p": 1,
"frequency_penalty": 0,
"presence_penalty": 0,
"n": 1,
"stream": false,
"stop": "<string>",
"extra_body": {
"metadata": {
"thinking_config": {
"thinking_budget": 123
},
"responseSchema": {
"type": "OBJECT",
"properties": {},
"required": [
"<string>"
]
}
}
}
}
'import requests
url = "https://mavi-backend.memories.ai/serve/api/v2/vu/chat/completions"
payload = {
"model": "gemini:gemini-2.5-flash",
"messages": [{ "content": "<string>" }],
"temperature": 0.7,
"max_tokens": 1000,
"top_p": 1,
"frequency_penalty": 0,
"presence_penalty": 0,
"n": 1,
"stream": False,
"stop": "<string>",
"extra_body": { "metadata": {
"thinking_config": { "thinking_budget": 123 },
"responseSchema": {
"type": "OBJECT",
"properties": {},
"required": ["<string>"]
}
} }
}
headers = {
"Authorization": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
model: 'gemini:gemini-2.5-flash',
messages: [{content: '<string>'}],
temperature: 0.7,
max_tokens: 1000,
top_p: 1,
frequency_penalty: 0,
presence_penalty: 0,
n: 1,
stream: false,
stop: '<string>',
extra_body: {
metadata: {
thinking_config: {thinking_budget: 123},
responseSchema: {type: 'OBJECT', properties: {}, required: ['<string>']}
}
}
})
};
fetch('https://mavi-backend.memories.ai/serve/api/v2/vu/chat/completions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://mavi-backend.memories.ai/serve/api/v2/vu/chat/completions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'model' => 'gemini:gemini-2.5-flash',
'messages' => [
[
'content' => '<string>'
]
],
'temperature' => 0.7,
'max_tokens' => 1000,
'top_p' => 1,
'frequency_penalty' => 0,
'presence_penalty' => 0,
'n' => 1,
'stream' => false,
'stop' => '<string>',
'extra_body' => [
'metadata' => [
'thinking_config' => [
'thinking_budget' => 123
],
'responseSchema' => [
'type' => 'OBJECT',
'properties' => [
],
'required' => [
'<string>'
]
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: <api-key>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://mavi-backend.memories.ai/serve/api/v2/vu/chat/completions"
payload := strings.NewReader("{\n \"model\": \"gemini:gemini-2.5-flash\",\n \"messages\": [\n {\n \"content\": \"<string>\"\n }\n ],\n \"temperature\": 0.7,\n \"max_tokens\": 1000,\n \"top_p\": 1,\n \"frequency_penalty\": 0,\n \"presence_penalty\": 0,\n \"n\": 1,\n \"stream\": false,\n \"stop\": \"<string>\",\n \"extra_body\": {\n \"metadata\": {\n \"thinking_config\": {\n \"thinking_budget\": 123\n },\n \"responseSchema\": {\n \"type\": \"OBJECT\",\n \"properties\": {},\n \"required\": [\n \"<string>\"\n ]\n }\n }\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://mavi-backend.memories.ai/serve/api/v2/vu/chat/completions")
.header("Authorization", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"model\": \"gemini:gemini-2.5-flash\",\n \"messages\": [\n {\n \"content\": \"<string>\"\n }\n ],\n \"temperature\": 0.7,\n \"max_tokens\": 1000,\n \"top_p\": 1,\n \"frequency_penalty\": 0,\n \"presence_penalty\": 0,\n \"n\": 1,\n \"stream\": false,\n \"stop\": \"<string>\",\n \"extra_body\": {\n \"metadata\": {\n \"thinking_config\": {\n \"thinking_budget\": 123\n },\n \"responseSchema\": {\n \"type\": \"OBJECT\",\n \"properties\": {},\n \"required\": [\n \"<string>\"\n ]\n }\n }\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://mavi-backend.memories.ai/serve/api/v2/vu/chat/completions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"model\": \"gemini:gemini-2.5-flash\",\n \"messages\": [\n {\n \"content\": \"<string>\"\n }\n ],\n \"temperature\": 0.7,\n \"max_tokens\": 1000,\n \"top_p\": 1,\n \"frequency_penalty\": 0,\n \"presence_penalty\": 0,\n \"n\": 1,\n \"stream\": false,\n \"stop\": \"<string>\",\n \"extra_body\": {\n \"metadata\": {\n \"thinking_config\": {\n \"thinking_budget\": 123\n },\n \"responseSchema\": {\n \"type\": \"OBJECT\",\n \"properties\": {},\n \"required\": [\n \"<string>\"\n ]\n }\n }\n }\n}"
response = http.request(request)
puts response.read_body{
"id": "resp_5810813e-99b9-427a-8736-23cf34573627",
"object": "completion",
"model": "gemini:gemini-2.5-flash",
"created_at": 1767093284,
"status": "completed",
"choices": [
{
"text": "This video demonstrates the effects of vacuum on three different states of oranges.\n\n1. **Setup**: Three orange-related items are placed in a vacuum chamber: a whole orange",
"index": 0
}
],
"usage": {
"input_tokens": 16983,
"output_tokens": 37,
"total_tokens": 17020
},
"meta": {
"provider": "gemini",
"provider_model": "gemini-2.5-flash"
}
}
Gemini Video
Generate chat completions using Gemini VLM model with text, video, and image inputs.
curl --request POST \
--url https://mavi-backend.memories.ai/serve/api/v2/vu/chat/completions \
--header 'Authorization: <api-key>' \
--header 'Content-Type: application/json' \
--data '
{
"model": "gemini:gemini-2.5-flash",
"messages": [
{
"content": "<string>"
}
],
"temperature": 0.7,
"max_tokens": 1000,
"top_p": 1,
"frequency_penalty": 0,
"presence_penalty": 0,
"n": 1,
"stream": false,
"stop": "<string>",
"extra_body": {
"metadata": {
"thinking_config": {
"thinking_budget": 123
},
"responseSchema": {
"type": "OBJECT",
"properties": {},
"required": [
"<string>"
]
}
}
}
}
'import requests
url = "https://mavi-backend.memories.ai/serve/api/v2/vu/chat/completions"
payload = {
"model": "gemini:gemini-2.5-flash",
"messages": [{ "content": "<string>" }],
"temperature": 0.7,
"max_tokens": 1000,
"top_p": 1,
"frequency_penalty": 0,
"presence_penalty": 0,
"n": 1,
"stream": False,
"stop": "<string>",
"extra_body": { "metadata": {
"thinking_config": { "thinking_budget": 123 },
"responseSchema": {
"type": "OBJECT",
"properties": {},
"required": ["<string>"]
}
} }
}
headers = {
"Authorization": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
model: 'gemini:gemini-2.5-flash',
messages: [{content: '<string>'}],
temperature: 0.7,
max_tokens: 1000,
top_p: 1,
frequency_penalty: 0,
presence_penalty: 0,
n: 1,
stream: false,
stop: '<string>',
extra_body: {
metadata: {
thinking_config: {thinking_budget: 123},
responseSchema: {type: 'OBJECT', properties: {}, required: ['<string>']}
}
}
})
};
fetch('https://mavi-backend.memories.ai/serve/api/v2/vu/chat/completions', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://mavi-backend.memories.ai/serve/api/v2/vu/chat/completions",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'model' => 'gemini:gemini-2.5-flash',
'messages' => [
[
'content' => '<string>'
]
],
'temperature' => 0.7,
'max_tokens' => 1000,
'top_p' => 1,
'frequency_penalty' => 0,
'presence_penalty' => 0,
'n' => 1,
'stream' => false,
'stop' => '<string>',
'extra_body' => [
'metadata' => [
'thinking_config' => [
'thinking_budget' => 123
],
'responseSchema' => [
'type' => 'OBJECT',
'properties' => [
],
'required' => [
'<string>'
]
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: <api-key>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://mavi-backend.memories.ai/serve/api/v2/vu/chat/completions"
payload := strings.NewReader("{\n \"model\": \"gemini:gemini-2.5-flash\",\n \"messages\": [\n {\n \"content\": \"<string>\"\n }\n ],\n \"temperature\": 0.7,\n \"max_tokens\": 1000,\n \"top_p\": 1,\n \"frequency_penalty\": 0,\n \"presence_penalty\": 0,\n \"n\": 1,\n \"stream\": false,\n \"stop\": \"<string>\",\n \"extra_body\": {\n \"metadata\": {\n \"thinking_config\": {\n \"thinking_budget\": 123\n },\n \"responseSchema\": {\n \"type\": \"OBJECT\",\n \"properties\": {},\n \"required\": [\n \"<string>\"\n ]\n }\n }\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://mavi-backend.memories.ai/serve/api/v2/vu/chat/completions")
.header("Authorization", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"model\": \"gemini:gemini-2.5-flash\",\n \"messages\": [\n {\n \"content\": \"<string>\"\n }\n ],\n \"temperature\": 0.7,\n \"max_tokens\": 1000,\n \"top_p\": 1,\n \"frequency_penalty\": 0,\n \"presence_penalty\": 0,\n \"n\": 1,\n \"stream\": false,\n \"stop\": \"<string>\",\n \"extra_body\": {\n \"metadata\": {\n \"thinking_config\": {\n \"thinking_budget\": 123\n },\n \"responseSchema\": {\n \"type\": \"OBJECT\",\n \"properties\": {},\n \"required\": [\n \"<string>\"\n ]\n }\n }\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://mavi-backend.memories.ai/serve/api/v2/vu/chat/completions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"model\": \"gemini:gemini-2.5-flash\",\n \"messages\": [\n {\n \"content\": \"<string>\"\n }\n ],\n \"temperature\": 0.7,\n \"max_tokens\": 1000,\n \"top_p\": 1,\n \"frequency_penalty\": 0,\n \"presence_penalty\": 0,\n \"n\": 1,\n \"stream\": false,\n \"stop\": \"<string>\",\n \"extra_body\": {\n \"metadata\": {\n \"thinking_config\": {\n \"thinking_budget\": 123\n },\n \"responseSchema\": {\n \"type\": \"OBJECT\",\n \"properties\": {},\n \"required\": [\n \"<string>\"\n ]\n }\n }\n }\n}"
response = http.request(request)
puts response.read_body{
"id": "resp_5810813e-99b9-427a-8736-23cf34573627",
"object": "completion",
"model": "gemini:gemini-2.5-flash",
"created_at": 1767093284,
"status": "completed",
"choices": [
{
"text": "This video demonstrates the effects of vacuum on three different states of oranges.\n\n1. **Setup**: Three orange-related items are placed in a vacuum chamber: a whole orange",
"index": 0
}
],
"usage": {
"input_tokens": 16983,
"output_tokens": 37,
"total_tokens": 17020
},
"meta": {
"provider": "gemini",
"provider_model": "gemini-2.5-flash"
}
}
https://mavi-backend.memories.ai/serve/api/v2
Auth: Authorization: sk-mavi-... (no Bearer prefix)POST https://mavi-backend.memories.ai/serve/api/v2/vu/chat/completionsVideo Understanding (VLM) endpoints use the /vu path prefix. Image Understanding (ILM) endpoints use /iu instead.Supported Models
All models require thegemini: prefix when used in the model parameter (e.g., gemini:gemini-2.5-flash).
Premium Models
| Model | Input Price | Output Price |
|---|---|---|
| gemini-3-pro-preview | $2/1M (≤200K), $4/1M (>200K) | $12/1M (≤200K), $18/1M (>200K) |
| gemini-2.5-pro | $1.25/1M (≤200K), $2.5/1M (>200K) | $10/1M (≤200K), $15/1M (>200K) |
Flash Models (High Performance)
| Model | Input Price | Output Price |
|---|---|---|
| gemini-3-flash-preview | $0.5/1M tokens | $3/1M tokens |
| gemini-2.5-flash | $0.30/1M tokens | $2.5/1M tokens |
| gemini-2.5-flash-preview-09-2025 | $0.30/1M tokens | $2.5/1M tokens |
| gemini-2.0-flash | $0.1/1M tokens | $0.4/1M tokens |
Lite Models (Cost-Effective)
| Model | Input Price | Output Price |
|---|---|---|
| gemini-2.5-flash-lite | $0.1/1M tokens | $0.4/1M tokens |
| gemini-2.5-flash-lite-preview-09-2025 | $0.1/1M tokens | $0.4/1M tokens |
| gemini-2.0-flash-lite | $0.075/1M tokens | $0.3/1M tokens |
gemini: prefix in your API calls:- ✅ Correct:
"model": "gemini:gemini-2.5-flash" - ❌ Incorrect:
"model": "gemini-2.5-flash"
Request Body
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| model | string | Yes | - | The model to use (e.g., gemini:gemini-2.5-flash) |
| messages | array | Yes | - | Array of message objects. Each message contains: - role: Role type, values: system, user, assistant- content: Message content. A plain string is accepted for text-only turns, but must be an array as soon as you include a video or image — passing a bare string with an input_file intent silently drops the media and the upstream model rejects the call with status: "errored". Array items can contain:- type: Content type, text or input_file- text: Text content (when type is text)- file_uri: File URL or base64 encoded file (when type is input_file). Note: video does not support base64- mime_type: MIME type of the file (e.g., image/jpeg, video/mp4) |
| temperature | number | No | 0.7 | Controls randomness: 0.0-2.0, higher = more random |
| max_tokens | integer | No | 1000 | Maximum number of tokens to generate |
| top_p | number | No | 1.0 | Nucleus sampling: 0.0-1.0, consider tokens with top_p probability mass |
| frequency_penalty | number | No | 0.0 | Reduces repetition of frequent tokens: -2.0 to 2.0 |
| presence_penalty | number | No | 0.0 | Increases likelihood of new topics: -2.0 to 2.0 |
| n | integer | No | 1 | Number of completions to generate |
| stream | boolean | No | false | Whether to stream the response |
| stop | string | array | null | No | null | Stop sequences. Can be a string, array of strings, or null |
| extra_body | object | No | - | Additional body parameters. Contains: - metadata: Metadata object- thinking_config: Thinking configuration- thinking_budget: Integer value for thinking budget- response_mime_type: Response MIME type (application/json or json_schema)- responseSchema: JSON schema object for structured output |
Code Example
from openai import OpenAI
client = OpenAI(
api_key="sk-mavi-...",
base_url="https://mavi-backend.memories.ai/serve/api/v2/vu"
)
def call_my_vlm():
resp = client.chat.completions.create(
model="gemini:gemini-2.5-flash", # e.g. gemini:gemini-3-flash-preview or gemini:gemini-2.5-flash
messages=[
{"role": "system", "content": "You are a multimodal assistant. Keep your answers concise."},
{
"role": "user",
"content": [
{
"type": "text",
"text": "Please summarize the content of this video and image"
},
{
"type": "input_file",
"file_uri": "https://storage.googleapis.com/memories-test-data/gun5.png", # base64 or url, video does not support base64
"mime_type": "image/jpeg"
},
{
"type": "input_file",
"file_uri": "https://storage.googleapis.com/memories-test-data/test_1min.mp4", # base64 or url, video does not support base64
"mime_type": "video/mp4"
}
]
}
],
temperature=0.7, # Controls randomness: 0.0-2.0, higher = more random
max_tokens=1000, # Maximum number of tokens to generate
top_p=1.0, # Nucleus sampling: 0.0-1.0, consider tokens with top_p probability mass
frequency_penalty=0.0, # -2.0 to 2.0, reduces repetition of frequent tokens
presence_penalty=0.0, # -2.0 to 2.0, increases likelihood of new topics
n=1, # Number of completions to generate
stream=False, # Whether to stream the response
stop=None, # Stop sequences (list of strings)
extra_body={
"metadata": {
"thinking_config": {
"thinking_budget": 1024
},
"response_mime_type": "application/json", # application/json, json_schema
"responseSchema": {
"type": "OBJECT",
"properties": {
"video_summary": {
"type": "STRING",
"description": "Summary of the video content from 1 second to 8 seconds."
},
"image_summary": {
"type": "STRING",
"description": "Summary of the image content."
}
},
"required": [
"video_summary",
"image_summary"
]
}
}
}
)
return resp
# Usage example
result = call_my_vlm()
print(result)
Response
Returns the chat completion response with structured output.{
"id": "resp_5810813e-99b9-427a-8736-23cf34573627",
"object": "completion",
"model": "gemini:gemini-2.5-flash",
"created_at": 1767093284,
"status": "completed",
"choices": [
{
"text": "This video demonstrates the effects of vacuum on three different states of oranges.\n\n1. **Setup**: Three orange-related items are placed in a vacuum chamber: a whole orange",
"index": 0
}
],
"usage": {
"input_tokens": 16983,
"output_tokens": 37,
"total_tokens": 17020
},
"meta": {
"provider": "gemini",
"provider_model": "gemini-2.5-flash"
}
}
Response Parameters
| Parameter | Type | Description |
|---|---|---|
| id | string | Unique identifier for the completion |
| object | string | Object type, always “completion” |
| model | string | The model used for the completion |
| created_at | integer | Unix timestamp of when the completion was created |
| status | string | Status of the completion (e.g., “completed”) |
| choices | array | Array of completion choices |
| choices[].text | string | Text content of the completion |
| choices[].index | integer | Index of the choice in the choices array |
| usage | object | Token usage information |
| usage.input_tokens | integer | Number of input tokens used |
| usage.output_tokens | integer | Number of output tokens generated |
| usage.total_tokens | integer | Total number of tokens used |
| meta | object | Metadata about the completion |
| meta.provider | string | Provider name (e.g., “gemini”) |
| meta.provider_model | string | Provider-specific model name |
Error Responses
HTTP 200 — not a 4xx/5xx. Check status and look for a top-level error object before parsing choices.{
"id": "resp_...",
"object": "completion",
"model": "gemini:gemini-2.0-flash-lite",
"status": "errored",
"choices": [],
"usage": {"input_tokens": 0, "output_tokens": 0, "total_tokens": 0},
"error": {
"code": "INVALID_ARGUMENT",
"message": "Unable to submit request because Model input cannot be empty. ...",
"http_status": 400
},
"meta": {
"provider": "gemini",
"provider_model": "gemini-2.0-flash-lite",
"provider_http_status": 400,
"provider_error": { "error": { "code": 400, "message": "...", "status": "INVALID_ARGUMENT" } }
}
}
| Field | Type | Description |
|---|---|---|
| status | string | "completed" on success, "errored" when the upstream model rejected the request. |
| error | object | Present only when status == "errored". Contains the upstream error normalized. |
| error.code | string | Provider-defined error code (e.g. INVALID_ARGUMENT). |
| error.message | string | Human-readable failure reason from the provider. |
| error.http_status | integer | The HTTP status the upstream provider would have returned (the mavi response is still 200). |
| meta.provider_http_status | integer | Mirror of error.http_status. |
| meta.provider_error | object | Raw provider error envelope, in case you need its native shape. |
status == "completed" (or that choices is non-empty) before reading choices[0].text.Gemini structured-output quirks
Two real behaviours to handle when you useextra_body.metadata.response_mime_type = "application/json":
- Gemini frequently wraps its JSON output in
```json ... ```markdown code fences. Strip them beforejson.loads. - On long videos with a strict schema, Gemini occasionally loops — emitting the same set of entries repeatedly until it hits
max_tokensand the JSON is left unclosed. Tolerate this by closing the array at the last complete}and deduplicating client-side.
examples/agent/02_restaurant_service_quality.py (extract_json and _dedup_events) for a worked example.Authorizations
Body
The model to use (e.g., gemini:gemini-2.5-flash)
"gemini:gemini-2.5-flash"
Array of message objects
Show child attributes
Show child attributes
Controls randomness: 0.0-2.0, higher = more random
0 <= x <= 2Maximum number of tokens to generate
Nucleus sampling: 0.0-1.0
0 <= x <= 1Reduces repetition of frequent tokens: -2.0 to 2.0
-2 <= x <= 2Increases likelihood of new topics: -2.0 to 2.0
-2 <= x <= 2Number of completions to generate
Whether to stream the response
Stop sequences
Show child attributes
Show child attributes
Response
Chat completion response
Unique identifier for the completion
"resp_5810813e-99b9-427a-8736-23cf34573627"
Object type, always 'completion'
"completion"
The model used for the completion
"gemini:gemini-2.5-flash"
Unix timestamp of when the completion was created
1767093284
Status of the completion
"completed"
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
