Video Generation

View as Markdown

Video Generation

Mesh API provides an async video generation endpoint that accepts text prompts, reference images, video clips, and audio tracks as inputs and returns a downloadable video URL.

Video generation is asynchronous. The POST endpoint creates a task and returns a task ID immediately. You then poll GET /v1/video/generations/{id} until the task reaches a terminal state, or receive the result passively via a webhook callback.

Base URL: https://api.meshapi.ai

Auth: Authorization: Bearer rsk_<your-key> on all requests.


Endpoints

MethodPathDescription
POST/v1/video/generationsCreate a video generation task
GET/v1/video/generations/{id}Get task status and result
GET/v1/video/generationsList your tasks

Create a Video Generation Task

POST /v1/video/generations

Submits a video generation request. Returns immediately with {"id": "<task_id>"}.

Request body

FieldTypeRequiredDescription
modelstringYesModel ID, e.g. byteplus/dreamina-seedance-2-0.
contentarrayYesArray of content items (see Content items).
callback_urlstringNoYour URL to receive a POST when the task completes.
resolutionstringNoOutput resolution, e.g. 720p, 1080p.
ratiostringNoAspect ratio, e.g. 16:9, 9:16, 1:1.
durationintegerNoTarget video duration in seconds.
framesintegerNoTarget frame count (use duration or frames, not both).
generate_audiobooleanNoGenerate an audio track alongside the video. Pricing differs when enabled.
return_last_framebooleanNoInclude a still image of the final frame in the result.
seedintegerNoReproducible generation seed.
camera_fixedbooleanNoLock the camera position (no camera movement).
watermarkbooleanNoAdd a provider watermark to the output.
draftbooleanNoGenerate a fast draft preview.
service_tierstringNoProvider service tier selection.
execution_expires_afterintegerNoSeconds until the task expires if not completed (default 172800 = 48 h).
priorityinteger 0–9NoTask priority hint. Higher = more urgent.
safety_identifierstringNoPass a safety policy identifier for content moderation.

Content items

The content array lets you combine different input modalities. Each item has a type field and a corresponding payload field.

Text prompt

1{
2 "type": "text",
3 "text": "A slow-motion shot of waves crashing on a rocky coastline at sunset"
4}

Image input (reference / first frame)

Pass a publicly accessible URL or a Base64-encoded image.

1{ "type": "image_url", "image_url": { "url": "https://example.com/reference.jpg" } }
1{ "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,/9j/4AAQ..." } }

Video input (reference video)

Seedance 2.0 series only. Pass a publicly accessible URL or a Base64-encoded video.

1{ "type": "video_url", "video_url": { "url": "https://example.com/clip.mp4" } }
1{ "type": "video_url", "video_url": { "url": "data:video/mp4;base64,AAAAIGZ0eXA..." } }

Audio input

Seedance 2.0 series only. Pass a publicly accessible URL or a Base64-encoded audio file.

Audio cannot be used as the sole input. At least one reference image or video must also be included in the content array.

1{ "type": "audio_url", "audio_url": { "url": "https://example.com/soundtrack.mp3" } }
1{ "type": "audio_url", "audio_url": { "url": "data:audio/mpeg;base64,SUQzBA..." } }

Input size limits

These limits apply to BytePlus Seedance models. Other providers may differ.

Video input

  • Max file size: 50 MB per video

Audio input

  • Supported formats: wav, mp3
  • Duration per clip: 2–15 seconds; up to 3 reference audio clips allowed, with a total combined duration of no more than 15 seconds
  • Max file size: 15 MB per audio file
  • Max total request body size: 64 MB

Do not use Base64 encoding for large files. Use a public URL instead to stay within the 64 MB request body limit.

For public URLs, the file must be reachable by the provider’s servers without authentication. For Base64 input, the data URI must include the MIME type prefix (e.g. data:video/mp4;base64,...).

Seedance input support matrix

Input typeAll Seedance modelsSeedance 2.0 series onlyPublic URLBase64
Text promptYes
ImageYesYesYes
VideoYesYesYes
AudioYes (requires image or video)YesYes

Response

1{ "id": "t-xxxxxxxxxxxxxxxx" }

Save the id to poll for status.

Example — text to video

$curl -X POST https://api.meshapi.ai/v1/video/generations \
> -H "Authorization: Bearer rsk_your_key" \
> -H "Content-Type: application/json" \
> -d '{
> "model": "byteplus/dreamina-seedance-2-0",
> "content": [
> {
> "type": "text",
> "text": "A cinematic drone shot over a misty mountain valley at dawn"
> }
> ],
> "resolution": "1080p",
> "ratio": "16:9",
> "duration": 5,
> "generate_audio": false,
> "return_last_frame": true,
> "callback_url": "https://yourapp.example.com/webhooks/video"
> }'

Example — image to video

1{
2 "model": "byteplus/dreamina-seedance-2-0",
3 "content": [
4 {
5 "type": "image_url",
6 "image_url": { "url": "https://example.com/first-frame.jpg" }
7 },
8 {
9 "type": "text",
10 "text": "The subject slowly turns and walks toward the camera"
11 }
12 ],
13 "duration": 5,
14 "ratio": "9:16"
15}

Example — video with audio input

1{
2 "model": "byteplus/dreamina-seedance-2-0",
3 "content": [
4 {
5 "type": "text",
6 "text": "A product reveal with dramatic lighting"
7 },
8 {
9 "type": "audio_url",
10 "audio_url": { "url": "https://example.com/background-music.mp3" }
11 }
12 ],
13 "duration": 8,
14 "generate_audio": false
15}

Get Task Status

GET /v1/video/generations/{id}

Returns the current state of a task. For non-terminal tasks, Mesh API fetches the latest status from the upstream provider and syncs its database before responding.

Task statuses

StatusMeaning
queuedTask accepted, not yet started.
runningGeneration in progress.
succeededComplete — content.video_url is populated.
failedError — check the error field.
expiredTask timed out before completing.
cancelledTask was cancelled.

Once a task reaches a terminal status (succeeded, failed, expired, cancelled), subsequent GET calls are served from the Mesh API database without hitting the upstream provider.

Response fields

FieldTypeDescription
idstringTask ID.
modelstringModel ID used.
statusstringCurrent task status.
content.video_urlstringDownloadable video URL (when succeeded).
content.last_frame_urlstringStill image of the final frame (when return_last_frame was set).
error.codestringError code (when failed).
error.messagestringHuman-readable error description (when failed).
usage.completion_tokensintegerTokens billed for the generation.
usage.total_tokensintegerTotal tokens (input + output).
seedintegerSeed used for the generation.
resolutionstringOutput resolution.
ratiostringAspect ratio.
durationintegerDuration in seconds.
framesintegerFrame count.
framespersecondintegerFrames per second of the output.
generate_audiobooleanWhether audio was generated.
created_atintegerUnix timestamp of task creation.
updated_atintegerUnix timestamp of last status update.

Example — polling until complete

1import time
2import requests
3
4TASK_ID = "t-xxxxxxxxxxxxxxxx"
5headers = {"Authorization": "Bearer rsk_your_key"}
6
7while True:
8 res = requests.get(
9 f"https://api.meshapi.ai/v1/video/generations/{TASK_ID}",
10 headers=headers
11 ).json()
12
13 status = res["status"]
14 print(f"Status: {status}")
15
16 if status == "succeeded":
17 print("Video URL:", res["content"]["video_url"])
18 break
19 elif status in ("failed", "expired", "cancelled"):
20 print("Error:", res.get("error"))
21 break
22
23 time.sleep(5)

Succeeded response

1{
2 "id": "t-xxxxxxxxxxxxxxxx",
3 "model": "byteplus/dreamina-seedance-2-0",
4 "status": "succeeded",
5 "content": {
6 "video_url": "https://cdn.example.com/videos/generated.mp4",
7 "last_frame_url": "https://cdn.example.com/videos/last-frame.jpg"
8 },
9 "usage": {
10 "completion_tokens": 200,
11 "total_tokens": 200
12 },
13 "resolution": "1080p",
14 "ratio": "16:9",
15 "duration": 5,
16 "frames": 150,
17 "framespersecond": 30,
18 "created_at": 1718000000,
19 "updated_at": 1718000180
20}

Failed response

1{
2 "id": "t-xxxxxxxxxxxxxxxx",
3 "model": "byteplus/dreamina-seedance-2-0",
4 "status": "failed",
5 "error": {
6 "code": "content_policy_violation",
7 "message": "The request could not be processed due to content policy."
8 }
9}

List Tasks

GET /v1/video/generations

Returns tasks stored in Mesh API’s database for your API key. This endpoint does not refresh task status from the upstream provider — use GET /v1/video/generations/{id} to force a live sync for an in-progress task.

Query parameters

ParameterTypeDescription
statusstring (repeatable)Filter by status. Multiple values combine with OR, e.g. ?status=running&status=succeeded.
modelstring (repeatable)Filter by model ID. Multiple values combine with OR.
created_afterISO 8601 datetimeOnly include tasks created at or after this timestamp.
created_beforeISO 8601 datetimeOnly include tasks created before this timestamp.
limitinteger 1–200Page size (default 50).
offsetintegerPagination offset (default 0).

Results are ordered by created_at descending (newest first).

Response

1{
2 "object": "list",
3 "data": [
4 {
5 "id": "t-xxxxxxxxxxxxxxxx",
6 "model": "byteplus/dreamina-seedance-2-0",
7 "status": "succeeded",
8 "created_at": 1718000000,
9 "updated_at": 1718000180,
10 "content": {
11 "video_url": "https://cdn.example.com/videos/generated.mp4"
12 }
13 }
14 ],
15 "has_more": false,
16 "total": 1,
17 "limit": 50,
18 "offset": 0
19}

Webhooks

Instead of polling, you can pass a callback_url in the create request. Mesh API will POST the full task payload to your URL when the task reaches a terminal status (succeeded, failed, or expired).

How it works

  1. You pass "callback_url": "https://yourapp.example.com/webhooks/video" in the create request.
  2. Mesh API intercepts the completion notification from the upstream provider, updates its database, and then forwards the completed task payload to your callback_url.
  3. Your endpoint receives a POST with a JSON body containing the final task state.

This means you never need to poll — just register a webhook endpoint and handle the event when it arrives.

Webhook payload

The payload is identical to the response from GET /v1/video/generations/{id}:

1{
2 "id": "t-xxxxxxxxxxxxxxxx",
3 "model": "byteplus/dreamina-seedance-2-0",
4 "status": "succeeded",
5 "content": {
6 "video_url": "https://cdn.example.com/videos/generated.mp4",
7 "last_frame_url": "https://cdn.example.com/videos/last-frame.jpg"
8 },
9 "usage": {
10 "completion_tokens": 200,
11 "total_tokens": 200
12 },
13 "generate_audio": false,
14 "resolution": "1080p",
15 "ratio": "16:9",
16 "duration": 5
17}

Handling the webhook

Your endpoint should:

  1. Return a 2xx response quickly. Mesh API fires the callback as fire-and-forget with a 10-second timeout — a slow response won’t block task completion on our side, but it will not be retried.
  2. Identify the task from id in the payload.
  3. Check status to decide what to do — download content.video_url on success, log or surface error on failure.
1from flask import Flask, request, jsonify
2
3app = Flask(__name__)
4
5@app.route("/webhooks/video", methods=["POST"])
6def video_webhook():
7 task = request.json
8 task_id = task["id"]
9 status = task["status"]
10
11 if status == "succeeded":
12 video_url = task["content"]["video_url"]
13 # save or process the video
14 print(f"Task {task_id} succeeded: {video_url}")
15 elif status == "failed":
16 error = task.get("error", {})
17 print(f"Task {task_id} failed: {error.get('code')} — {error.get('message')}")
18 elif status == "expired":
19 print(f"Task {task_id} expired before completing")
20
21 return jsonify({"ok": True}), 200

Webhook vs polling — when to use each

ApproachBest for
WebhookProduction apps, background pipelines — no polling overhead, event-driven.
PollingQuick scripts, testing, environments where you can’t expose a public endpoint.

You can use both simultaneously: pass a callback_url for production delivery and still poll GET /v1/video/generations/{id} as a fallback. Mesh API deduplicates usage logging so you are never billed twice.


Pricing

Video generation pricing is token-based and varies by the type of output:

Output typeBilling basis
Video onlycompletion_tokens from the usage response
Video + audio (generate_audio: true)Higher per-token rate
Video with video inputHigher per-token rate

The exact per-token rates for each model are listed on the Pricing page.


Error reference

HTTP statusMeaning
200Task created or retrieved successfully.
401Missing or invalid API key.
402Insufficient balance or spend cap reached.
422The model is not supported for video generation.
429Rate limit exceeded.
502Upstream provider error.
503Video generation service temporarily unavailable.

When a task itself fails (HTTP 200, status: "failed"), the error.code and error.message fields describe the reason from the provider (e.g. content_policy_violation, invalid_input).