Start Video Stream Moderation
curl --request POST \
--url https://mavi-backend.memories.ai/serve/api/v2/stream/start \
--header 'Authorization: <api-key>' \
--header 'Content-Type: application/json' \
--data '
{
"url": "rtmp://example.com/live/stream",
"enable_content_moderation": false,
"enable_logo_detection": false,
"fps": 1,
"max_logos": 5
}
'import requests
url = "https://mavi-backend.memories.ai/serve/api/v2/stream/start"
payload = {
"url": "rtmp://example.com/live/stream",
"enable_content_moderation": False,
"enable_logo_detection": False,
"fps": 1,
"max_logos": 5
}
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({
url: 'rtmp://example.com/live/stream',
enable_content_moderation: false,
enable_logo_detection: false,
fps: 1,
max_logos: 5
})
};
fetch('https://mavi-backend.memories.ai/serve/api/v2/stream/start', 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/stream/start",
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([
'url' => 'rtmp://example.com/live/stream',
'enable_content_moderation' => false,
'enable_logo_detection' => false,
'fps' => 1,
'max_logos' => 5
]),
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/stream/start"
payload := strings.NewReader("{\n \"url\": \"rtmp://example.com/live/stream\",\n \"enable_content_moderation\": false,\n \"enable_logo_detection\": false,\n \"fps\": 1,\n \"max_logos\": 5\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/stream/start")
.header("Authorization", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"url\": \"rtmp://example.com/live/stream\",\n \"enable_content_moderation\": false,\n \"enable_logo_detection\": false,\n \"fps\": 1,\n \"max_logos\": 5\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://mavi-backend.memories.ai/serve/api/v2/stream/start")
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 \"url\": \"rtmp://example.com/live/stream\",\n \"enable_content_moderation\": false,\n \"enable_logo_detection\": false,\n \"fps\": 1,\n \"max_logos\": 5\n}"
response = http.request(request)
puts response.read_body{
"code": 200,
"message": "success",
"data": {
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"message": "Stream processing started successfully"
}
}
{
"code": 200,
"message": "SUCCESS",
"data": {
"status": 0,
"message": "Segment processed successfully",
"data": {
"segment_index": 9,
"start_time": "45",
"end_time": "50",
"video_url": "https://storage.googleapis.com/memories-cache/video-stream/f9c9b4caf3184ac4bcc46727d802e3d4/segment_0009.mp4",
"content_moderation": {
"has_unsafe_content": false,
"frames_processed": 10
},
"logo_detection": {
"has_logo": false,
"logos": [],
"frames_processed": 10
},
"processing_time_ms": "9594"
},
"token": {
"input": 2946,
"output": 25,
"total": 2971,
"video_tokens": 2946,
"text_tokens": 25
}
},
"task_id": "f9c9b4caf3184ac4bcc46727d802e3d4"
}
{
"code": 200,
"message": "SUCCESS",
"data": {
"status": 402,
"message": "You don't have enough points.",
"data": null,
"token": null
},
"task_id": "f9c9b4caf3184ac4bcc46727d802e3d4"
}
{
"code": 200,
"message": "SUCCESS",
"data": {
"status": 14,
"message": "Stream stopped by user",
"data": null,
"token": null
},
"task_id": "f9c9b4caf3184ac4bcc46727d802e3d4"
}
{
"code": 200,
"message": "SUCCESS",
"data": {
"status": 15,
"message": "Stream ended: no video data received",
"data": null,
"token": null
},
"task_id": "f9c9b4caf3184ac4bcc46727d802e3d4"
}
{
"code": 200,
"message": "SUCCESS",
"data": {
"status": 16,
"message": "Server capacity reached. Please try again later.",
"data": null,
"token": null
},
"task_id": "f9c9b4caf3184ac4bcc46727d802e3d4"
}
Live Video Content Moderation
Start Video Stream Moderation
Start video stream content moderation and logo detection with real-time processing.
POST
/
stream
/
start
Start Video Stream Moderation
curl --request POST \
--url https://mavi-backend.memories.ai/serve/api/v2/stream/start \
--header 'Authorization: <api-key>' \
--header 'Content-Type: application/json' \
--data '
{
"url": "rtmp://example.com/live/stream",
"enable_content_moderation": false,
"enable_logo_detection": false,
"fps": 1,
"max_logos": 5
}
'import requests
url = "https://mavi-backend.memories.ai/serve/api/v2/stream/start"
payload = {
"url": "rtmp://example.com/live/stream",
"enable_content_moderation": False,
"enable_logo_detection": False,
"fps": 1,
"max_logos": 5
}
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({
url: 'rtmp://example.com/live/stream',
enable_content_moderation: false,
enable_logo_detection: false,
fps: 1,
max_logos: 5
})
};
fetch('https://mavi-backend.memories.ai/serve/api/v2/stream/start', 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/stream/start",
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([
'url' => 'rtmp://example.com/live/stream',
'enable_content_moderation' => false,
'enable_logo_detection' => false,
'fps' => 1,
'max_logos' => 5
]),
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/stream/start"
payload := strings.NewReader("{\n \"url\": \"rtmp://example.com/live/stream\",\n \"enable_content_moderation\": false,\n \"enable_logo_detection\": false,\n \"fps\": 1,\n \"max_logos\": 5\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/stream/start")
.header("Authorization", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"url\": \"rtmp://example.com/live/stream\",\n \"enable_content_moderation\": false,\n \"enable_logo_detection\": false,\n \"fps\": 1,\n \"max_logos\": 5\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://mavi-backend.memories.ai/serve/api/v2/stream/start")
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 \"url\": \"rtmp://example.com/live/stream\",\n \"enable_content_moderation\": false,\n \"enable_logo_detection\": false,\n \"fps\": 1,\n \"max_logos\": 5\n}"
response = http.request(request)
puts response.read_body{
"code": 200,
"message": "success",
"data": {
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"message": "Stream processing started successfully"
}
}
{
"code": 200,
"message": "SUCCESS",
"data": {
"status": 0,
"message": "Segment processed successfully",
"data": {
"segment_index": 9,
"start_time": "45",
"end_time": "50",
"video_url": "https://storage.googleapis.com/memories-cache/video-stream/f9c9b4caf3184ac4bcc46727d802e3d4/segment_0009.mp4",
"content_moderation": {
"has_unsafe_content": false,
"frames_processed": 10
},
"logo_detection": {
"has_logo": false,
"logos": [],
"frames_processed": 10
},
"processing_time_ms": "9594"
},
"token": {
"input": 2946,
"output": 25,
"total": 2971,
"video_tokens": 2946,
"text_tokens": 25
}
},
"task_id": "f9c9b4caf3184ac4bcc46727d802e3d4"
}
{
"code": 200,
"message": "SUCCESS",
"data": {
"status": 402,
"message": "You don't have enough points.",
"data": null,
"token": null
},
"task_id": "f9c9b4caf3184ac4bcc46727d802e3d4"
}
{
"code": 200,
"message": "SUCCESS",
"data": {
"status": 14,
"message": "Stream stopped by user",
"data": null,
"token": null
},
"task_id": "f9c9b4caf3184ac4bcc46727d802e3d4"
}
{
"code": 200,
"message": "SUCCESS",
"data": {
"status": 15,
"message": "Stream ended: no video data received",
"data": null,
"token": null
},
"task_id": "f9c9b4caf3184ac4bcc46727d802e3d4"
}
{
"code": 200,
"message": "SUCCESS",
"data": {
"status": 16,
"message": "Server capacity reached. Please try again later.",
"data": null,
"token": null
},
"task_id": "f9c9b4caf3184ac4bcc46727d802e3d4"
}
Product: Visual Intelligence — Live Video Content Moderation
Use case: Real-time NSFW / violence / logo detection on a live video stream (RTMP/RTSP), results pushed to your callback every 5s
Mode: Server-Pull (you give us a stream URL → we pull → results to your callback)
Host:
https://mavi-backend.memories.ai/serve/api/v2
Auth: Authorization: sk-mavi-... (no Bearer prefix)Access Required: To use this API endpoint, please contact us at contact@memories.ai to enable stream processing features for your account.
Pricing:The pricing varies based on the features enabled:Content Moderation + Logo Detection:
Cost (USD) = duration × 2 × (1 + (fps - 1) × 0.5) / 3600
- Base rate: $2.00/hour
- Example (FPS=2): 3.00/hour(0.00417 per 5-second segment)
Cost (USD) = duration × 1.5 × (1 + (fps - 1) × 0.5) / 3600
- Base rate: $1.50/hour
- Example (FPS=2): 2.25/hour(0.00313 per 5-second segment)
Cost (USD) = duration × 1.5 × (1 + (fps - 1) × 0.5) / 3600
- Base rate: $1.50/hour
- Example (FPS=2): 2.25/hour(0.00313 per 5-second segment)
duration: Video duration in seconds (5 seconds per segment)fps: Frames per second- Charges occur at start (pre-charge for one segment) and per segment processed
Supported Protocols
- RTMP (Recommended)
- RTSP
Key Features
- ✅ Real-time stream processing
- ✅ Automatic 5-second segmentation
- ✅ Asynchronous webhook callbacks
- ✅ Content moderation (adult content, violence, etc.)
- ✅ Logo detection and tracking
- ✅ Automatic resource management
Code Example
import requests
BASE_URL = "https://mavi-backend.memories.ai/serve/api/v2"
API_KEY = "sk-mavi-..."
HEADERS = {
"Authorization": f"{API_KEY}",
"Content-Type": "application/json"
}
def start_video_stream(stream_url: str, fps: int = 2):
url = f"{BASE_URL}/stream/start"
data = {
"url": stream_url,
"enable_content_moderation": True,
"enable_logo_detection": True,
"fps": fps,
"max_logos": 10
}
resp = requests.post(url, json=data, headers=HEADERS)
return resp.json()
# Usage example
result = start_video_stream("rtmp://example.com/live/stream", fps=2)
print(result)
print(f"Task ID: {result['data']['task_id']}")
const axios = require('axios');
const BASE_URL = 'https://mavi-backend.memories.ai/serve/api/v2';
const API_KEY = 'sk-mavi-...';
const headers = {
'Authorization': API_KEY,
'Content-Type': 'application/json'
};
async function startVideoStream(streamUrl, fps = 2) {
const response = await axios.post(
`${BASE_URL}/stream/start`,
{
url: streamUrl,
enable_content_moderation: true,
enable_logo_detection: true,
fps: fps,
max_logos: 10
},
{ headers }
);
return response.data;
}
// Usage example
startVideoStream('rtmp://example.com/live/stream', 2)
.then(result => {
console.log(result);
console.log(`Task ID: ${result.data.task_id}`);
});
curl -X POST "https://mavi-backend.memories.ai/serve/api/v2/stream/start" \
-H "Authorization: sk-mavi-..." \
-H "Content-Type: application/json" \
-d '{
"url": "rtmp://example.com/live/stream",
"enable_content_moderation": true,
"enable_logo_detection": true,
"fps": 2,
"max_logos": 10
}'
Response
Returns the task information for the started video stream.{
"code": 200,
"message": "success",
"data": {
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"message": "Stream processing started successfully"
}
}
{
"code": 200,
"message": "SUCCESS",
"data": {
"status": 0,
"message": "Segment processed successfully",
"data": {
"segment_index": 9,
"start_time": "45",
"end_time": "50",
"video_url": "https://storage.googleapis.com/memories-cache/video-stream/f9c9b4caf3184ac4bcc46727d802e3d4/segment_0009.mp4",
"content_moderation": {
"has_unsafe_content": false,
"frames_processed": 10
},
"logo_detection": {
"has_logo": false,
"logos": [],
"frames_processed": 10
},
"processing_time_ms": "9594"
},
"token": {
"input": 2946,
"output": 25,
"total": 2971,
"video_tokens": 2946,
"text_tokens": 25
}
},
"task_id": "f9c9b4caf3184ac4bcc46727d802e3d4"
}
{
"code": 200,
"message": "SUCCESS",
"data": {
"status": 402,
"message": "You don't have enough points.",
"data": null,
"token": null
},
"task_id": "f9c9b4caf3184ac4bcc46727d802e3d4"
}
{
"code": 200,
"message": "SUCCESS",
"data": {
"status": 14,
"message": "Stream stopped by user",
"data": null,
"token": null
},
"task_id": "f9c9b4caf3184ac4bcc46727d802e3d4"
}
{
"code": 200,
"message": "SUCCESS",
"data": {
"status": 15,
"message": "Stream ended: no video data received",
"data": null,
"token": null
},
"task_id": "f9c9b4caf3184ac4bcc46727d802e3d4"
}
{
"code": 200,
"message": "SUCCESS",
"data": {
"status": 16,
"message": "Server capacity reached. Please try again later.",
"data": null,
"token": null
},
"task_id": "f9c9b4caf3184ac4bcc46727d802e3d4"
}
Request Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| url | string | Yes | - | Video stream URL (supports RTMP/RTSP/HLS) |
| enable_content_moderation | boolean | No | false | Enable content moderation |
| enable_logo_detection | boolean | No | false | Enable logo detection |
| fps | integer | No | 1 | Frames per second to process |
| max_logos | integer | No | 5 | Maximum number of logos to detect (1-50) |
Response Parameters
| Parameter | Type | Description |
|---|---|---|
| code | string | Response code (200 indicates success) |
| message | string | Response message describing the operation result |
| data | object | Response data object containing task information |
| data.task_id | string | Unique identifier of the stream processing task |
| data.message | string | Status message about the stream start |
Callback Response Parameters
Callbacks are sent to your configured webhook URL every time a 5-second segment is processed.| Parameter | Type | Description |
|---|---|---|
| code | string | Response code (200 indicates success) |
| message | string | Response message (“SUCCESS”) |
| task_id | string | The task ID associated with this stream |
| data | object | Wrapper object containing processing results |
| data.status | integer | Status code (see Status Codes section below) |
| data.message | string | Status message describing the result |
| data.data | object | Segment processing results (null for error statuses) |
| data.data.segment_index | integer | Index of the processed segment (starts from 0) |
| data.data.start_time | string | Segment start time in seconds |
| data.data.end_time | string | Segment end time in seconds |
| data.data.video_url | string | URL of the processed video segment |
| data.data.content_moderation | object | Content moderation results |
| data.data.content_moderation.has_unsafe_content | boolean | Whether unsafe content was detected |
| data.data.content_moderation.frames_processed | integer | Number of frames processed |
| data.data.logo_detection | object | Logo detection results |
| data.data.logo_detection.has_logo | boolean | Whether logos were detected |
| data.data.logo_detection.logos | array | Array of detected logos (empty if none) |
| data.data.logo_detection.frames_processed | integer | Number of frames processed |
| data.data.processing_time_ms | string | Processing time in milliseconds |
| data.token | object | Token usage statistics (null for error statuses) |
| data.token.input | integer | Number of input tokens used |
| data.token.output | integer | Number of output tokens generated |
| data.token.total | integer | Total tokens used |
| data.token.video_tokens | integer | Tokens from video processing |
| data.token.text_tokens | integer | Tokens from text processing |
Status Codes
| Status | Name | Description | Stream Stopped |
|---|---|---|---|
| 0 | Success | Segment processed successfully | No |
| -1 | Error | Processing failed | No |
| 14 | User Stopped | User manually stopped the stream | Yes |
| 15 | No Data | Stream has no video data | Yes |
| 16 | Capacity Reached | Server capacity limit reached | Yes |
| 402 | Insufficient Balance | User balance insufficient | Yes |
Callback Flow
User Request → Start Stream → Process Segment (every 5s) → Webhook Callback
↓
Pre-charge for first segment
↓
Charge per segment processed
Important Notes
- Webhook Configuration: Configure your webhook URL in user settings before using this API
- No callback parameter needed: The system uses a unified asynchronous callback mechanism
- Automatic segmentation: Every 5 seconds triggers a callback
- Pre-charge mechanism: Balance is checked and pre-charged at start
- Per-segment charging: Each processed segment is charged immediately
- Auto-stop on insufficient balance: Stream automatically stops if balance is insufficient (status 402)
- Concurrent stream limit: Each user has a maximum concurrent stream limit
- Retry mechanism: Failed callbacks are automatically retried
Rate Limiting
- Maximum concurrent streams: Each user can run N concurrent stream tasks (video + audio combined)
- Capacity check: Returns status 16 if server capacity is reached
- Balance check: Returns HTTP 402 if insufficient balance at start
Authorizations
Body
application/json
Video stream URL (supports RTMP/RTSP)
Example:
"rtmp://example.com/live/stream"
Enable content moderation
Enable logo detection
Frames per second to process
Required range:
x >= 1Maximum number of logos to detect (1-50)
Required range:
1 <= x <= 50