> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apimart.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 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.

> [!NOTE]
>
> This API is asynchronous. Read `data[0].task_id` after submission, then use [Get task status](https://docs.apimart.ai/en/api-reference/tasks/status.md) to retrieve the result. Generation typically takes 1–3 minutes; poll every 5–10 seconds. Actual completion time depends on the task.

```bash cURL theme={null}
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
  }'
```

```python Python theme={null}
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())
```

```javascript JavaScript theme={null}
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());
```

```json 200 theme={null}
{
  "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

**Authorization** `string` *required*

Use Bearer authentication: `Bearer <token>`, where `<token>` is your APIMart API key. Send the body with `Content-Type: application/json`.

## Request parameters

**model** `string` *required*

Must be `apimart-zoe`.

**prompt** `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.

**resolution** `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.

**aspect\_ratio** `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.

**size** `string`

Alternative name for `aspect_ratio`, with the same values. Prefer sending only one of these fields. Omit for final generation.

**duration** `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.

**seconds** `integer`

Alias for `duration`, with the same rules. Prefer sending only one of these fields. Omit for final generation.

**audio** `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.

**generate\_audio** `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.

**seed** `integer`

Random seed, as an integer. Do not send for final generation.

**nsfw\_check** `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.

**omni\_reference\_task\_type** `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.

**return\_last\_frame** `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.

**output\_format** `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.

**response\_format** `string`

Alias for `output_format`, accepting `mp4` / `mov`. If both are sent, `output_format` takes precedence. Omit for final generation.

**enhance\_prompt** `boolean`

Set `true` to automatically optimize the prompt before generation. Off by default: the original prompt is used. Omit for final generation.

**image\_with\_roles** `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.

**image\_urls** `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.

**first\_frame\_image** `string`

First-frame image address, equivalent to the `first_frame` role in `image_with_roles`.

**last\_frame\_image** `string`

Last-frame image address, equivalent to the `last_frame` role in `image_with_roles`. A first frame is also required.

**video\_urls** `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.

**audio\_urls** `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.

**draft** `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.

**draft\_task\_id** `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 public `http(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. |

Video dimensions, pixel area, aspect ratio, exact duration and frame rate, plus reference audio duration, are checked at submission. Invalid media returns synchronous HTTP 400 with no charge. Video validation uses `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_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.
* 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.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.

> [!WARNING]
>
> 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

```json theme={null}
{
  "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
}
```

You can also provide frames using `first_frame_image` and `last_frame_image`. Do not add reference images, videos or audio to the same request.

### Image, video and audio references

```json theme={null}
{
  "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
}
```

Images in `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.

```json theme={null}
{
  "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"
}
```

Editing uses the reference-video rate: reserve 30 seconds of output, then reconcile using the actual output duration rounded to a whole second, including reference-video duration under the billing formula below.

### Last-frame image, MOV output and prompt optimization

```json theme={null}
{
  "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

1. Submit a draft with `draft: true`. Omit `resolution` to use `480p`, or explicitly set it to `480p`; other values are not supported.

```json theme={null}
{
  "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
}
```

2. Wait for the draft to succeed, then submit its `task_id` for final generation. Only `model` and `draft_task_id` are needed. If you specify `resolution`, it must be `1080p`.

```json theme={null}
{
  "model": "apimart-zoe",
  "draft_task_id": "task_01KZOEEXAMPLE"
}
```

> [!WARNING]
>
> 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

```bash cURL theme={null}
curl --request GET \
  --url https://api.apimart.ai/v1/tasks/task_01KZOEEXAMPLE \
  --header 'Authorization: Bearer <token>'
```

Submission returns `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.

```json theme={null}
{
  "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
  }
}
```

For failed tasks, read `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](https://apimart.ai/pricing) 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))`. `ceil` rounds up to a whole second. Use the rate for generation with reference videos.
* For example, requesting 8 seconds keeps `D = 8` even if the video lasts 8.2 seconds. An automatic duration of 8.5 seconds gives `D = 9`. Reference clips of 2.8 and 3.8 seconds are summed to 6.6 and then rounded down, giving `R = 6`.
* Drafts use the `480p` rate and final videos the `1080p` rate, 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 an `error` 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. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.