Video Material Search (for Smart Editing)
Search the copyrighted video material library with a single natural-language scene description or keywords, and get back clips ready to drop onto a timeline — including the best-matching in-clip time ranges (segments), a direct media URL, duration, orientation and fps. Returns synchronously, no polling required.
Designed for smart editing / auto-assembly: query material shot-by-shot from a storyboard, then stitch onto the timeline.
Search Endpoint
Basic Info
| Item | Value |
|---|
| Method | POST |
| Path | /task/video_clip_search |
| Content-Type | application/json |
| Auth | Authorization header (pass the API Key directly) |
| Response | Synchronous search results (not an async task) |
Request Body
| Field | Type | Required | Default | Description |
|---|
query | string | Yes | — | Scene description (natural language) or keywords |
top_k | integer | No | 10 | Number of results, max 50 |
diversity | boolean | No | false | De-duplicate near-identical shots |
request_id | string | No | — | uuid, echoed back, for idempotency / tracing |
filters | object | No | — | Editing-oriented filters, see below |
filters Fields
| Field | Type | Description |
|---|
min_duration | number | Minimum clip duration (seconds) |
max_duration | number | Maximum clip duration (seconds) |
orientation | string | landscape / portrait / square, to match the output canvas |
min_width | integer | Minimum resolution width (pixels) |
exclude_ids | array<integer> | Material ids to exclude (already used in the same cut) |
Request Example
curl -X POST https://api.ai-mcn.tv:10000/task/video_clip_search \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "silhouette of a person with a camera on a coastal rock at sunset",
"top_k": 10,
"diversity": true,
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"filters": {
"min_duration": 3,
"orientation": "landscape",
"min_width": 1920
}
}'
Success Response Example
{
"code": 200,
"msg": "success",
"data": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"recalled": 84,
"results": [
{
"clip_id": 2693564728807342,
"score": 0.87,
"url": "https://cdn.example.com/nmcn/material/video_clip/raw/2693564728807342.mp4?expires=1750086000&sign=Xa1b2C3d4E5f6G7h8I9j0k",
"cover_url": "https://cdn.example.com/nmcn/material/video_clip/keyframe/2693564728807342/cover.jpg",
"preview_url": "https://cdn.example.com/nmcn/material/video_clip/preview/2693564728807342.mp4",
"duration": 12.4,
"width": 3840,
"height": 2160,
"fps": 25,
"orientation": "landscape",
"segments": [
{ "start": 2.0, "end": 8.5, "best": 5.2, "score": 0.91 }
],
"matched": { "facets": { "time_of_day": ["dusk"], "scene": ["coast"] } },
"note": "Dusk by the sea; a person on the rocky shore filming the water."
}
]
}
}
Response Fields
| Field | Type | Description |
|---|
recalled | integer | Total recalled (before filtering), for observability |
results[].clip_id | integer | Material id. Note: this is a large integer that may exceed JS Number.MAX_SAFE_INTEGER (2^53) — JavaScript consumers should treat it as a string (e.g. quote the field at the response-text level before parsing), otherwise precision loss can collide ids of different materials |
results[].score | number | Relevance score (after rerank, sorted descending) |
results[].url | string | Temporary media URL (mp4, expires in 24h via ?expires=&sign=; re-search for a fresh link after expiry) |
results[].cover_url | string | Representative cover image (unsigned, never expires) |
results[].preview_url | string | Low-res preview proxy (mp4, width ≤640, no audio track; unsigned, never expires). Ideal for list previews and a proxy-editing workflow: rough-cut with the small proxy first, then download the original via url when finalizing |
results[].duration | number | Duration (seconds) |
results[].width / height | integer | Resolution (pixels) |
results[].fps | number | Frame rate |
results[].orientation | string | Canvas orientation |
results[].segments | array | In-clip matching ranges (sorted by score; first = best) |
results[].segments[].start / end | number | Range bounds (seconds), ready for the timeline |
results[].segments[].best | number | Best-matching timestamp within the range (seconds) |
results[].matched.facets | object | Matched visual attributes (scene / time-of-day, etc.) |
results[].note | string | Material text description |
Error Codes
| Code | HTTP | Description | Resolution |
|---|
400 | 400 | Malformed request body (body is not a valid JSON object) | Check the request body |
6502 | 401 | Authentication failed | Check the API Key in the Authorization header |
6401 | 500 | Upstream search service unavailable, or request parameters rejected by the search layer (e.g. empty query, invalid orientation/filters values) | Verify request parameters first; if correct, retry later |
6402 | 500 | Upstream search service timeout | Retry later |
Determine success/failure from the code field in the response body, not the HTTP status code (top_k overflow does not error — it is clamped to 50).
Limits
top_k is capped at 50 (clamped automatically).
- This endpoint takes a single query; for multiple shots, call it concurrently (batch input is a future capability).
- Searches the copyrighted library only; material quality level is not differentiated.
- The media
url expires in 24h; re-search for a fresh link after expiry (cover_url / preview_url are unsigned and never expire).
segments are based on keyframes sampled every 1.5s, so localization accuracy is about ±1.5s.