# Video generation

Video is asynchronous: submit a task, poll it, then fetch the produced file.
Every video model is served by a task plugin that speaks its vendor upstream,
while your client keeps one OpenAI-compatible video surface; the platform stores
the task and serves the render through its own URL.

## Task plugins

Video models are grouped by the task plugin that serves them. The plugin decides
the upstream API, which request fields the model accepts, and which usage facts
the charge is computed from — the model page in the Model Market names them for
the model you call. Call any of them through the same video endpoints below.

| Plugin | Serves |
| --- | --- |
| Alibaba Bailian | Wan · HappyHorse |
| Doubao | Seedance video · Seedream image |
| Hailuo Video | MiniMax Hailuo · MiniMax-H3 |
| Kling | Kling v1 · v1.6 · v2-master |
| Sora | sora-2 · sora-2-pro |
| Vidu | Vidu Q1 · Q2 · 2.0 · 1.5 |
| Jimeng | Jimeng video |
| Veo (Gemini API) | Veo 3.0 · 3.1 |
| Veo (Vertex AI) | Veo 3.0 · 3.1 |

## Submit, poll, fetch

1. `POST /v1/videos` — returns a task id immediately; the clip is rendered upstream afterwards.
2. `GET /v1/videos/{task_id}` — the `status` field moves `queued` → `in_progress` → `completed` (or `failed`, with the upstream reason). Poll every 5–10 seconds; a provider that queues for minutes reports `queued` until it starts.
3. `GET /v1/videos/{task_id}/content` — streams the produced file through the platform with your token. Append `?download=1` to receive it as an attachment (`Content-Disposition`) instead of an inline stream. Range requests are forwarded to the source, so a player can seek without downloading the whole file.
4. `GET /v1/tasks/{task_id}/artifacts` — lists what the task produced: key, media type and a signed `content_url`. The signed URL opens without a token, so it can go straight into a player or an `img` tag, and it is the surface to use when a model produces something other than a video.

```bash
TASK=$(curl -s "https://sharellm.net/v1/videos" \
  -H "Authorization: Bearer $SHARE_LLM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "<model-id>",
    "prompt": "a lighthouse beam sweeping across a foggy harbour",
    "seconds": 5,
    "size": "1280x720"
  }' | jq -r .id)

curl "https://sharellm.net/v1/videos/$TASK" -H "Authorization: Bearer $SHARE_LLM_TOKEN"
curl -o out.mp4 "https://sharellm.net/v1/videos/$TASK/content" \
  -H "Authorization: Bearer $SHARE_LLM_TOKEN"
```

## Request fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `model` | string | yes | Video model ID from the market. |
| `prompt` | string | yes | Scene, motion and style description. Required on every request, image-to-video included. |
| `seconds` | integer | no | Clip length in whole seconds, within the model range (commonly 4–15). The model default applies when it is omitted. |
| `size` | string | no | Frame size, for example `1280x720`. Drives both the resolution class and the aspect ratio; a model that renders preset resolutions also accepts them as `metadata.resolution`. |
| `input_reference` | string | no | Image used as the first frame (image-to-video). |
| `images` | array | no | Two images become the first and last frame; three or more become reference material. A model that cannot express one of those modes rejects the request before the task is created. |
| `metadata` | object | no | Model-specific extras — ratio, resolution, duration, watermark, provider-specific parameters and full content arrays. The common keys are listed below; a plugin may read keys of its own, and its model page names them. |

## Metadata keys

| Key | Type | Description |
| --- | --- | --- |
| `metadata.ratio` | string | Aspect ratio: `21:9`, `16:9`, `4:3`, `1:1`, `3:4` or `9:16`, plus adaptive on models that inherit the ratio from an image or a video. |
| `metadata.resolution` | string | Resolution class, for example `480p`, `720p`, `1080p`, `768P` or `2K` — the values the model renders. Overrides the class implied by `size`. |
| `metadata.duration` | integer | Clip length in seconds, same range as `seconds` and wins when both are sent. |
| `metadata.content` | array | Full provider content array — text, `first_frame` / `last_frame` / reference images, audio and video references — forwarded after validation. |
| `metadata.first_frame_image` | string | First frame, spelled explicitly instead of `input_reference` or `images`. |
| `metadata.last_frame_image` | string | Last frame; send it together with a first frame. |
| `metadata.reference_video` | string \| array | Up to 3 reference videos, on the models that take reference material. |
| `metadata.reference_audio` | string \| array | Up to 3 reference audios, on the models that take reference material. |
| `metadata.parameters` | object | Vendor extras forwarded verbatim, for example `{"generate_audio": true, "watermark": false}`. |

## Response

```json
// submit
{"id": "task_...", "object": "video", "model": "...", "status": "queued", "progress": 0, "created_at": 1789827220}

// poll (the provider's own snapshot follows the fields the platform owns)
{
  "id": "task_...",
  "object": "video",
  "model": "...",
  "status": "completed",
  "progress": 100,
  "completed_at": 1789827269
}

// artifacts
{
  "task_id": "task_...",
  "artifacts": [
    {"key": "video", "type": "video", "mime_type": "video/mp4",
     "content_url": "https://sharellm.net/v1/tasks/task_.../artifacts/video/content?access=..."}
  ]
}
```

## Notes

- **The file stays on the provider's side.** The content endpoint reads the asset from the provider that rendered it: the platform proxies it and keeps no second copy. Fetch a finished video promptly. If the provider has already removed its copy, the request answers `410` (gone) or `502` (upstream error) and the task has to be submitted again.
- **Input modes cannot be mixed.** First-frame inputs, first-and-last-frame inputs and reference material are different modes: send one or the other. Invalid combinations and out-of-range durations are rejected before the task is created, so nothing is charged.
- **Coming from the provider's own API?** A provider content array goes into `metadata.content` unchanged, and the provider top-level duration, resolution, ratio, watermark and provider-specific keys go into `metadata` under the same names. `size` and `seconds` remain available as the platform spellings. One difference: the provider treats the text item as optional, this platform requires `prompt` on every request.
- **One plugin per model.** A model is served by one plugin, so a field that plugin cannot express is rejected with a `400` instead of being silently dropped — for example an upload where the plugin only forwards media URLs, or a middle frame on a model that has no such input. The model page in the Model Market is the authoritative list for the model you call.

## Billing

The plugin reports the usage facts of the task — generated seconds, resolution
class, reference media — and the model price is an expression over them, settled
when the task completes. The charge breakdown on a usage-log row shows the facts
and the price that applied.

A channel may sell only fixed clip lengths. Such a model declares its supported
durations; asking for any other length is refused with `400
unsupported_duration` (or `400 duration_required` when no length is named)
before the task is created and before anything is charged.