curl --request POST \
--url https://api.apimart.ai/v1/videos/generations \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"model": "apimart-zoe",
"prompt": "An orange kitten looks toward the camera from a windowsill, with sunlight from the side and a slow camera push-in",
"resolution": "1080p",
"aspect_ratio": "16:9",
"duration": 8,
"audio": true,
"nsfw_check": true,
"return_last_frame": true
}'
import requests
response = requests.post(
"https://api.apimart.ai/v1/videos/generations",
headers={"Authorization": "Bearer <token>"},
json={
"model": "apimart-zoe",
"prompt": "An orange kitten looks toward the camera from a windowsill, with sunlight from the side and a slow camera push-in",
"resolution": "1080p",
"aspect_ratio": "16:9",
"duration": 8,
"audio": True,
"nsfw_check": True,
"return_last_frame": True
}
)
response.raise_for_status()
print(response.json())
const response = await fetch("https://api.apimart.ai/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer <token>",
"Content-Type": "application/json"
},
body: JSON.stringify({
"model": "apimart-zoe",
"prompt": "An orange kitten looks toward the camera from a windowsill, with sunlight from the side and a slow camera push-in",
"resolution": "1080p",
"aspect_ratio": "16:9",
"duration": 8,
"audio": true,
"nsfw_check": true,
"return_last_frame": true
})
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
{
"code": 200,
"data": [
{
"status": "submitted",
"task_id": "task_01KZOEEXAMPLE"
}
]
}
apimart-zoe
apimart-zoe Video Generation
Generate and edit videos using text, frames or image/video/audio references, with 480p–4k output, last-frame images, MOV and draft-to-final generation.
POST
/
v1
/
videos
/
generations
curl --request POST \
--url https://api.apimart.ai/v1/videos/generations \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"model": "apimart-zoe",
"prompt": "An orange kitten looks toward the camera from a windowsill, with sunlight from the side and a slow camera push-in",
"resolution": "1080p",
"aspect_ratio": "16:9",
"duration": 8,
"audio": true,
"nsfw_check": true,
"return_last_frame": true
}'
import requests
response = requests.post(
"https://api.apimart.ai/v1/videos/generations",
headers={"Authorization": "Bearer <token>"},
json={
"model": "apimart-zoe",
"prompt": "An orange kitten looks toward the camera from a windowsill, with sunlight from the side and a slow camera push-in",
"resolution": "1080p",
"aspect_ratio": "16:9",
"duration": 8,
"audio": True,
"nsfw_check": True,
"return_last_frame": True
}
)
response.raise_for_status()
print(response.json())
const response = await fetch("https://api.apimart.ai/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer <token>",
"Content-Type": "application/json"
},
body: JSON.stringify({
"model": "apimart-zoe",
"prompt": "An orange kitten looks toward the camera from a windowsill, with sunlight from the side and a slow camera push-in",
"resolution": "1080p",
"aspect_ratio": "16:9",
"duration": 8,
"audio": true,
"nsfw_check": true,
"return_last_frame": true
})
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
{
"code": 200,
"data": [
{
"status": "submitted",
"task_id": "task_01KZOEEXAMPLE"
}
]
}
This API is asynchronous. Read
data[0].task_id after submission, then use Get task status to retrieve the result. Generation typically takes 1–3 minutes; poll every 5–10 seconds. Actual completion time depends on the task.curl --request POST \
--url https://api.apimart.ai/v1/videos/generations \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"model": "apimart-zoe",
"prompt": "An orange kitten looks toward the camera from a windowsill, with sunlight from the side and a slow camera push-in",
"resolution": "1080p",
"aspect_ratio": "16:9",
"duration": 8,
"audio": true,
"nsfw_check": true,
"return_last_frame": true
}'
import requests
response = requests.post(
"https://api.apimart.ai/v1/videos/generations",
headers={"Authorization": "Bearer <token>"},
json={
"model": "apimart-zoe",
"prompt": "An orange kitten looks toward the camera from a windowsill, with sunlight from the side and a slow camera push-in",
"resolution": "1080p",
"aspect_ratio": "16:9",
"duration": 8,
"audio": True,
"nsfw_check": True,
"return_last_frame": True
}
)
response.raise_for_status()
print(response.json())
const response = await fetch("https://api.apimart.ai/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer <token>",
"Content-Type": "application/json"
},
body: JSON.stringify({
"model": "apimart-zoe",
"prompt": "An orange kitten looks toward the camera from a windowsill, with sunlight from the side and a slow camera push-in",
"resolution": "1080p",
"aspect_ratio": "16:9",
"duration": 8,
"audio": true,
"nsfw_check": true,
"return_last_frame": true
})
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
{
"code": 200,
"data": [
{
"status": "submitted",
"task_id": "task_01KZOEEXAMPLE"
}
]
}
Choose a mode
| Mode | Parameters |
|---|---|
| Text to video | Provide prompt without media. |
| First/last frame to video | Use the first_frame and optional last_frame roles in image_with_roles, or use first_frame_image / last_frame_image. |
| Reference to video | Use reference images or videos, optionally with reference audio. Audio alone is not supported. |
| Draft | Set draft: true for 480p output. |
| Final video | Use draft_task_id from a successful draft for 1080p output. |
| Video editing | Set omni_reference_task_type: "edit" and provide at least one source video. Output duration and aspect ratio follow the source. |
Headers
string
required
Use Bearer authentication:
Bearer <token>, where <token> is your APIMart API key. Send the body with Content-Type: application/json.Request parameters
string
required
Must be
apimart-zoe.string
Up to 204,800 characters. Required for text-to-video; optional when providing media alone. Do not send this field with
draft_task_id: the final video inherits the draft prompt.string
Accepts
480p, 720p, 1080p, 2k and 4k, case-insensitively. Normal generation defaults to 720p. With draft: true, only 480p is supported and is used when omitted. With draft_task_id, only 1080p is supported and is used when omitted.string
Accepts
21:9, 16:9, 4:3, 1:1, 3:4, 9:16 and adaptive. Defaults to adaptive, determined by the model or media. Omit for final generation, which inherits the draft aspect ratio.For video editing (edit), omit this field or use adaptive to follow the source video.string
Alternative name for
aspect_ratio, with the same values. Prefer sending only one of these fields. Omit for final generation.integer
Normal generation: Integer seconds from
4 to 30, default 5. Explicit or default durations are billed as requested, with no adjustment to the finished video length. With -1, the model chooses: reserve 30 seconds at submission, then reconcile using the actual output duration rounded to the nearest whole second. Omit for final generation, which inherits the draft duration.For video editing (edit), omit this field or use -1. Omission is treated as -1; output duration follows the source video.integer
Alias for
duration, with the same rules. Prefer sending only one of these fields. Omit for final generation.boolean
Boolean controlling whether to generate audio with the video. Alias:
generate_audio. If omitted, the model decides. Do not send for final generation. This controls output audio, unlike the input references in audio_urls.If both audio and generate_audio are sent, their values must match or HTTP 400 is returned. When omitted, the model decides and usually includes sound.boolean
Alias for
audio, with the same rules. Prefer sending only one of these fields. Omit for final generation.If both audio and generate_audio are sent, their values must match or HTTP 400 is returned. When omitted, the model decides and usually includes sound.integer
Random seed, as an integer. Do not send for final generation.
boolean
Moderation is off by default; set
true to enable it.Optional boolean. Set true to enable content moderation for prompt, image_urls, image_with_roles, first/last-frame images and asset:// image assets. Reference videos and audio are not moderated.Final-video requests contain no prompt or media. In the draft-to-final workflow, moderation occurs when generating the draft.A match returns HTTP 400 (nsfw_content_detected), with the categories in error.message. Display this as a moderation rejection rather than a generic parameter error. If the moderation service fails or is unavailable, generation proceeds; enabling this parameter does not guarantee that every request completes moderation.string
Supports
auto (default, equivalent to omission; the model decides), reference and edit. reference / edit require reference media; using either for text-only or first/last-frame requests returns HTTP 400. edit requires at least one entry in video_urls and cannot be combined with draft: true. extend is unsupported and returns HTTP 400. Omit for final generation.boolean
Set
true to return a PNG image of the final frame in data.result.last_frame_url. Off by default. Omit for final generation.string
Supports
mp4 (default) or mov (QuickTime container). Other values return HTTP 400. Alias: response_format; if both are sent, output_format takes precedence. Omit for final generation.string
Alias for
output_format, accepting mp4 / mov. If both are sent, output_format takes precedence. Omit for final generation.boolean
Set
true to automatically optimize the prompt before generation. Off by default: the original prompt is used. Omit for final generation.object[]
Each item contains
url and role. Roles: first_frame, last_frame, reference_image. At most one first frame and one last frame; a last frame requires a first frame. First/last frames cannot be combined with reference media.Do not send image_urls and image_with_roles together. Doing so returns HTTP 400 (invalid_request_error) at submission, with no charge. The aliases image, images and image_url count as image_urls and cannot bypass this restriction.string[]
Up to 30 reference images. See the media requirements below for formats, dimensions and file sizes.Do not send
image_urls and image_with_roles together. Doing so returns HTTP 400 (invalid_request_error) at submission, with no charge. The aliases image, images and image_url count as image_urls and cannot bypass this restriction.string
First-frame image address, equivalent to the
first_frame role in image_with_roles.string
Last-frame image address, equivalent to the
last_frame role in image_with_roles. A first frame is also required.string[]
Up to 10 reference videos. Accepts public
http(s) URLs or asset://<asset ID>, but not data: Base64. Video dimensions, exact duration and frame rate are validated at submission using the rules below.For billing, clip durations are summed and then rounded down to whole seconds, with no later adjustment. This rounding is not used to validate individual clip durations. Inaccessible media or failed duration probing returns HTTP 400 (input_video_probe_failed) with no charge.auto / reference: 2–15 seconds, accepting 1.8 ≤ t ≤ 15.2. Source videos for edit: 4–30 seconds, accepting 3.8 ≤ t < 30.2. Validate the exact duration without rounding first.string[]
Up to 10; WAV / MP3; 2–15 seconds and
≤ 15 MB each, up to 30 seconds total. A ±0.2-second tolerance allows 1.8 ≤ t ≤ 15.2 per clip and ≤ 30.2 in total. Requires a reference image or video.Accepts public http(s) URLs or asset://<asset ID>, not data: Base64. Audio duration is checked at submission; invalid durations return synchronous HTTP 400 with no charge.boolean
Set
true to generate a 480p draft. Other parameters follow normal generation rules. Do not send this field when generating the final video.Video editing (edit) cannot generate drafts.string
The
task_id of a successful draft to generate a 1080p final video. Use the draft within 24 hours of its creation. The final video inherits its prompt, media, duration and aspect ratio; do not resend those fields. Final generation also forbids seed, audio and its alias generate_audio.Media addresses and combination limits
Images, videos and audio accept publichttp(s) URLs or private APIMart assets via asset://<asset ID>. None accept data: Base64; upload media first. Registration is automatic, with no separate media API call. Registration failures fail the task and refund charges.
| Media | Requirements |
|---|---|
| Reference images | Up to 30; JPEG / PNG / WebP / BMP / TIFF / GIF / HEIC / HEIF; aspect ratio 0.4–2.5; width and height each 300–6000 pixels; < 30 MB each. |
| Reference videos | Up to 10; MP4 / MOV (H.264 / H.265; audio track: AAC / MP3); ≤ 200 MB each. See the dimension, duration and frame-rate rules below. |
| Reference audio | Up to 10; WAV / MP3; 2–15 seconds and ≤ 15 MB each, up to 30 seconds total. A ±0.2-second tolerance allows 1.8 ≤ t ≤ 15.2 per clip and ≤ 30.2 in total. Requires a reference image or video. |
Reference video validation
| Property | Requirement |
|---|---|
| Width and height | Width and height must each be 300–6000 pixels. |
| Pixel area (width × height) | 409,600–8,295,044 pixels. |
| Aspect ratio (width / height) | 0.4–2.5. |
| Duration per clip | auto / reference: 2–15 seconds, accepting 1.8 ≤ t ≤ 15.2. Source videos for edit: 4–30 seconds, accepting 3.8 ≤ t < 30.2. Validate the exact duration without rounding first. |
| Frame rate | Round to the nearest integer (0.5 up), then check 24–60fps. Both 23.976 and 59.94 qualify. |
invalid_request_error and includes measured values. Use pixel area, not a minimum short edge of 480 and maximum long edge of 4096.
For dimensions alone, 420×1000 (420,000 pixels) qualifies. 720×480 (345,600 pixels), 848×480 (407,040 pixels) and 4096×2160 (8,847,360 pixels) fail the area limits. Duration, frame rate and all other media requirements must also be met.
Image dimensions, file size and encoding are checked during generation. Invalid media fails the task with an automatic refund. Check these properties before calling the API when possible.
Media combination rules
- Do not send
image_urlsandimage_with_rolestogether. Doing so returns HTTP 400 (invalid_request_error) at submission, with no charge. The aliasesimage,imagesandimage_urlcount asimage_urlsand cannot bypass this restriction. - First/last frames cannot appear in the same request as reference images, videos or audio.
- At most one first frame and one last frame. A last frame requires a first frame.
- Up to 30 reference images, 10 reference videos and 10 reference audio clips, with a combined maximum of 50 media items.
- Violating these combination or count limits causes a synchronous HTTP 400 at submission, with no charge.
omni_reference_task_type: "edit"requires at least one source video (3.8 ≤ t < 30.2seconds). Omitduration/secondsor set-1; omitaspect_ratio/sizeor setadaptive. Do not combine withdraft: true. Auto/reference modes retain the 2–15-second limit and its tolerance.
The following fields return HTTP 400 whenever present, even with
"", [], false or null; remove the fields entirely: negative_prompt, camera_fixed, camerafixed, extend_from_task_id, mask_url, video_list, element_list, multi_shot, tools. Omit n or set it to 1.Request examples
Media URLs below are placeholders. Replace them with accessible media addresses.First and last frames
{
"model": "apimart-zoe",
"prompt": "Transition naturally from the first frame to the last frame, keeping the lighting consistent",
"image_with_roles": [
{
"url": "https://example.com/start.jpg",
"role": "first_frame"
},
{
"url": "https://example.com/end.jpg",
"role": "last_frame"
}
],
"duration": 6
}
first_frame_image and last_frame_image. Do not add reference images, videos or audio to the same request.
Image, video and audio references
{
"model": "apimart-zoe",
"prompt": "The character in the reference image moves naturally in the reference video scene, with background music matching the visual rhythm",
"image_urls": [
"https://example.com/character.jpg"
],
"video_urls": [
"https://example.com/scene.mp4"
],
"audio_urls": [
"https://example.com/bgm.mp3"
],
"duration": -1
}
image_urls are reference images. With duration: -1, the model chooses the output duration.
Video editing
omni_reference_task_type: "edit" requires at least one source video (3.8 ≤ t < 30.2 seconds). Omit duration / seconds or set -1; omit aspect_ratio / size or set adaptive. Do not combine with draft: true. Auto/reference modes retain the 2–15-second limit and its tolerance.
{
"model": "apimart-zoe",
"prompt": "Turn the scene into a black-and-white classic film while preserving the camera movement and composition",
"video_urls": [
"https://example.com/source_20s.mp4"
],
"omni_reference_task_type": "edit"
}
Last-frame image, MOV output and prompt optimization
{
"model": "apimart-zoe",
"prompt": "An orange kitten looks toward the camera from a windowsill, with sunlight from the side and a slow camera push-in",
"duration": 5,
"return_last_frame": true,
"output_format": "mov",
"enhance_prompt": true
}
Draft → final video
- Submit a draft with
draft: true. Omitresolutionto use480p, or explicitly set it to480p; other values are not supported.
{
"model": "apimart-zoe",
"prompt": "An orange kitten looks toward the camera from a windowsill, with sunlight from the side and a slow camera push-in",
"draft": true
}
- Wait for the draft to succeed, then submit its
task_idfor final generation. Onlymodelanddraft_task_idare needed. If you specifyresolution, it must be1080p.
{
"model": "apimart-zoe",
"draft_task_id": "task_01KZOEEXAMPLE"
}
The final video inherits the draft prompt, media, duration, aspect ratio, seed and audio settings. Send
model, draft_task_id, optional resolution: "1080p", and common fields such as nsfw_check / webhook. The following generation fields must be absent; even empty strings/arrays, false or null return HTTP 400: prompt, image_urls, image_with_roles, image, images, image_url, first_frame_image, last_frame_image, video_urls, audio_urls, duration, seconds, aspect_ratio, size, seed, audio, generate_audio, draft, omni_reference_task_type, return_last_frame, output_format, response_format, enhance_prompt. Use the draft within 24 hours of creation. Draft and final generation are billed separately.Query tasks and results
cURL
curl --request GET \
--url https://api.apimart.ai/v1/tasks/task_01KZOEEXAMPLE \
--header 'Authorization: Bearer <token>'
data as an array; the task query returns data as an object. Check data.status: pending means queued, processing means generating, completed means success, and failed means failure. On success, read the video address from the data.result.videos[].url array, for example data.result.videos[0].url[0]. Videos are copied to APIMart storage. The example below shows only the key fields of a successful query.
{
"code": 200,
"data": {
"id": "task_01KZOEEXAMPLE",
"status": "completed",
"result": {
"videos": [
{
"url": [
"https://example.com/generated-video.mp4"
],
"expires_at": 1794475442
}
],
"last_frame_url": "https://example.com/generated-last-frame.png"
},
"progress": 100,
"completed": 1794389042
}
}
data.error.message for the reason. Charges are refunded automatically.
| Field | Description |
|---|---|
data.progress | Task progress, 0–100. |
data.result.videos[].url | Video address array, copied to APIMart storage. |
data.result.videos[].expires_at | The address expires 24 hours after task completion. Download the video promptly. |
data.result.last_frame_url | PNG last-frame image URL copied to APIMart storage. Present only on success when the request included return_last_frame: true. |
data.error.message | Failure reason; present only for failed tasks. |
data.error.code | The string task_failed for failed tasks. |
data.cost | Task cost; 0 for failed tasks after the reserved charge has been refunded. |
Billing rules
Billing is per second, with rates determined by output resolution and whether reference videos are used. See the pricing page for current rates and account discounts.D below is the output duration used for billing, in seconds.
| Case | Billable output duration D |
|---|---|
Normal generation: Explicit duration: 4–30, or omitted (default 5) | Bill the requested seconds; do not adjust to the finished video length. |
duration: -1 / video editing edit | Reserve 30 seconds, then reconcile using the actual output duration rounded to the nearest whole second. |
| Final video | Inherit the draft duration. If the draft used duration: -1, use its actual finished duration as the basis, without readjusting to the final video length. |
- Total reference video duration
R: probe each clip at submission, sum the durations, then round the sum down to whole seconds. It is not adjusted later. - Without reference videos: billable seconds =
D. With reference videos: billable seconds =D + max(R, ceil(D × 2/3)).ceilrounds up to a whole second. Use the rate for generation with reference videos. - For example, requesting 8 seconds keeps
D = 8even if the video lasts 8.2 seconds. An automatic duration of 8.5 seconds givesD = 9. Reference clips of 2.8 and 3.8 seconds are summed to 6.6 and then rounded down, givingR = 6. - Drafts use the
480prate and final videos the1080prate, billed separately. If a draft used reference videos, their original duration is also billed for the final video; no re-upload is needed. - Failed submissions are not charged. Failed tasks are automatically refunded.
Common errors
Failed submissions return anerror object. Read error.message for details and error.code for the string error code, distinct from the HTTP status. Failed submissions are not charged.
| HTTP | error.code | Cause and action |
|---|---|---|
| 400 | invalid_request_error | Check duration, resolution, aspect ratio, media counts/combinations, unsupported parameters and draft/final constraints. Also returned when draft_task_id is missing from storage, is not a draft, is unfinished or is over 24 hours old. Also covers invalid reference video dimensions, exact duration or frame rate, and combining image_urls (including aliases) with image_with_roles. Video validation errors include measured values. Also check reference audio duration, task type/editing constraints, output_format, matching audio aliases, and the presence of forbidden fields. |
| 400 | nsfw_content_detected | Content moderation rejected the request. Categories appear in error.message. Display a moderation rejection and revise the content before resubmitting. No charge. |
| 400 | input_video_probe_failed | Reference video is inaccessible or its duration cannot be probed. Check the address and content. |
| 402 | payment_required | Insufficient balance; automatic duration reserves 30 seconds. |
| 503 | draft_channel_unavailable / draft_task_unavailable | The draft generation channel is unavailable or the draft lookup temporarily failed. Retry later or generate a new draft. |