> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.beatapi.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.beatapi.io/_mcp/server.

# Ecommerce Video

> Create an Ecommerce Video workflow task from product images and an explicit output duration.

`POST https://api.beatapi.io/v1/ecommerce-video/tasks`

Create an Ecommerce Video workflow task from product images and an explicit output duration.

## Authorization

`Authorization`   `string`   **required**

Send your BeatAPI API key as a Bearer token. Create one on the [Dashboard](https://beatapi.io/dashboard).

```
Authorization: Bearer <BEATAPI_API_KEY>
```

Keep permanent API keys on trusted servers. `Content-Type: application/json` is required for the request body. `Idempotency-Key` is optional when the endpoint exposes it and is recommended for safe retries with the exact same body.

## Request body

`images`   `string[]`   **required**

Primary product or scene image first, followed by up to six additional public HTTPS PNG, JPEG, or WebP product or lifestyle images. Upload local files with `POST /v1/files` and use the returned `data.url`.

**Count:** 1 to 7 items

---

`duration`   `integer`   **required**

Required target output duration in seconds and the basis for USD calculation. Allowed range is 10-60 seconds.

**Range:** 10 to 60

---

`prompt`   `string`   **optional**

Optional creative direction, audience, product benefit, offer, tone, scenes, or call to action. Maximum 2000 characters.

**Length:** at most 2000 characters

---

`aspect_ratio`   `enum<string>`   **optional**

Target output placement. Use 16:9 for landscape, 9:16 for vertical social, or 1:1 for square placements; set explicitly for stable layout.

**Available options:** `16:9`, `9:16`, `1:1`

---

`language`   `enum<string>`   **optional**

Dialogue and narration language. Use `en` for English or `zh` for Chinese; set explicitly when the prompt contains mixed languages.

**Available options:** `en`, `zh`

## Response

A successful request returns HTTP `201`.

```json
{
  "data": {
    "id": "task_p9Lm2",
    "object": "task",
    "task_kind": "workflow",
    "capability_id": "ecommerce-video",
    "capability_version": 1,
    "workflow": "ecommerce-video",
    "status": "queued",
    "stage": "queued",
    "created_at": 1782210000,
    "updated_at": 1782210000,
    "completed_at": null,
    "output": null,
    "usage": {
      "credits_reserved": 4.5,
      "credits_charged": 4.5,
      "billable_duration_seconds": 15,
      "credits_settled": 0,
      "credits_refunded": 0
    },
    "request_id": "req_def456",
    "error_code": null,
    "error_message": null
  }
}
```

## Next step

Save `data.id` from the accepted response and poll `GET /v1/tasks/{task_id}` every 5–10 seconds until `status` is `succeeded` or `failed`. Read finished assets from `data.output.media`.

## Errors

| Status | Meaning                                                                                                                                                                                                                                                                                    |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `400`  | Malformed JSON or a field, value or combination this endpoint does not accept (`bad_request`; the message names the field), or input refused by moderation (`content_policy_violation`). Not retryable unchanged.                                                                          |
| `401`  | No API key (`missing_api_key`), or a key that is wrong, expired, disabled or exhausted (`invalid_api_key`; the message says which). Send it as `Authorization: Bearer <key>`; the key works with or without the `sk-` prefix. Not retryable.                                               |
| `402`  | Insufficient balance (`insufficient_credits`). The account balance is zero or below the price of this request. Top up, then send it again; not retryable before that.                                                                                                                      |
| `403`  | The key or account is not permitted to make this call (`forbidden`), for example a model that is not available on free credit (a top-up unlocks it), a request from outside the key's IP allowlist, or a model outside the key's group. Not retryable.                                     |
| `409`  | The `Idempotency-Key` was already used with a different request body or path within 24 hours, or the first request with it is still running (`idempotency_conflict`). Not retryable unchanged.                                                                                             |
| `429`  | Too many requests (`rate_limit_exceeded`, including the free-model limits). Retryable after `Retry-After` seconds (also `error.retry_after_seconds`).                                                                                                                                      |
| `500`  | Unexpected internal error (`internal_error`). Retryable; keep `request_id` for support if it persists.                                                                                                                                                                                     |
| `503`  | The request could not be completed (`processing_unavailable`, `processing_timeout`, `processing_failed` or `result_transfer_failed`). Nothing was charged. Retryable with backoff; a 503 carries no `Retry-After` header and no `retry_after_seconds`. BeatAPI does not answer 502 or 504. |