# Wan 3.0 Prime — /videos/generations

Wan 3.0 Prime: the same model as Wan 3.0, rendered faster, billed per second. Text-to-video, first frame, first + last frame, multi-image reference and reference-video edits on one endpoint; 2–30 seconds, 480P / 720P / 1080P. Async jobs; failures are not billed.

**Endpoint:** `POST https://nezhagate.com/v1/videos/generations`

## Quick start

Three steps: submit a job, poll it, take the video link. Replace YOUR_API_KEY with your key and the commands below run as they are.

**Step 1 · Submit (returns a job id at once, HTTP 202)**

```
curl https://nezhagate.com/v1/videos/generations \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "wan3.0-video-prime", "prompt": "A shiba inu chases falling cherry blossoms under a tree, bright sunlight, slow motion", "duration": 5, "resolution": "720P", "size": "16:9"}'
```

Response (HTTP 202)

```
{
  "id": "img_5c2e8f1a9b3d7e6f0a4c8b2d",
  "object": "video.generation.job",
  "status": "queued",
  "created": 1791281400,
  "model": "wan3.0-video-prime"
}
```

**Step 2 · Poll every 5–10 seconds until `status` is `succeeded` or `failed`**

```
curl https://nezhagate.com/v1/videos/jobs/img_5c2e8f1a9b3d7e6f0a4c8b2d \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Response when it succeeds

```
{
  "id": "img_5c2e8f1a9b3d7e6f0a4c8b2d",
  "object": "video.generation.job",
  "status": "succeeded",
  "created": 1791281400,
  "model": "wan3.0-video-prime",
  "data": [
    {"url": "https://img.nezhagate.com/i/8c1f0e5d2b7a49e3a6d0c4f1b9e2a7d5.mp4"}
  ],
  "usage": {"clips": 1, "model": "wan3.0-video-prime"}
}
```

**Step 3 · `data[0].url` is the clip (MP4). It is kept for 15 days; download it to your own storage to keep it longer.**

**Complete example (Python: submit, wait, save to disk)**

```
import time
import requests

BASE = "https://nezhagate.com/v1"
HEADERS = {"Authorization": "Bearer YOUR_API_KEY"}

# 1. submit
job = requests.post(f"{BASE}/videos/generations", headers=HEADERS, json={
    "model": "wan3.0-video-prime",
    "prompt": "A shiba inu chases falling cherry blossoms under a tree, bright sunlight, slow motion",
    "duration": 5,
    "resolution": "720P",
    "size": "16:9",
}).json()
if "error" in job:
    raise SystemExit(job["error"]["message"])

# 2. poll every 10 seconds, for up to an hour
deadline = time.time() + 3600
while job["status"] not in ("succeeded", "failed") and time.time() < deadline:
    time.sleep(10)
    job = requests.get(f"{BASE}/videos/jobs/{job['id']}", headers=HEADERS).json()

# 3. download the video
if job["status"] == "succeeded":
    url = job["data"][0]["url"]
    with open("wan3.0-video-prime.mp4", "wb") as f:
        f.write(requests.get(url).content)
    print("saved", url)
elif job["status"] == "failed":
    print("failed:", job["error"]["code"], job["error"]["message"])
else:
    print("still", job["status"], job["id"])
```

While queued, the poll also returns `queue_position` (jobs ahead of yours) and `eta_seconds` (expected wait). If your time limit passes, do not resubmit: the job is still there, so keep polling the same id later.

## Authentication
```
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
```

## At a glance

| Duration | 2–30 s |
| --- | --- |
| Resolution | 480P / 720P / 1080P |
| Aspect ratio | 16:9 / 9:16 / 1:1 / 4:3 / 3:4 / adaptive |
| Reference images | up to 10 · one as the first frame, two can be first + last frame, more as references |
| Reference videos | up to 5, 15 s in total |
| Reference audio | up to 5 |
| Sound | decided by the model, no switch (`audio: false` is refused) |
| Prompt length | up to 5,000 bytes (UTF-8; about 1,666 Chinese characters) |
| Billing | per second: the rate × (output seconds + reference-video seconds) |

## Request parameters

The JSON body of `POST /v1/videos/generations`:

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| model | string | Yes | The model id: `wan3.0-video-prime`. |
| prompt | string | Yes | What happens in the clip: picture, camera and motion. Up to 5,000 bytes (UTF-8; a Chinese character counts as 3, so about 1,666 of them). |
| duration | integer | No | Output length in seconds: any integer from 2 to 30; 2 when omitted. Alias `seconds`. With reference videos, output plus reference seconds may not exceed 30. |
| resolution | string | No | Resolution: 480P / 720P / 1080P; 720P when omitted. Keep the P (`720` without it is refused). |
| size | string | No | Aspect ratio: 16:9 / 9:16 / 1:1 / 4:3 / 3:4; 16:9 when omitted. With a first frame you can send `adaptive` so the clip follows the image. Alias `aspect_ratio`. |
| images | array | No | Reference images, up to 10: one becomes the first frame; two are references by default, or first + last frame with `image_role: first_frame`; three or more are references. Name them as `@Image1` in the prompt. A single image may go in `image`. Human-face review restriction: avoid reference material containing real human faces; realistic faces can also trigger detection. `FaceDetectionSuspect` means a possible real human face was detected. `DataInspectionFailed` means the input material did not pass content review, not that the service is unavailable. Replace the reference before resubmitting rather than repeatedly retrying unchanged input; contact support about suspected false positives. Failed tasks are not billed. |
| image_role | string | No | How images are used: `first_frame` (first frame, or first + last frame; 2 images at most) or `reference`. When omitted, one image is a first frame and two or more are references; with reference videos only `reference` works. |
| video_references | array | No | Reference videos, up to 5, each written as `{"url": "...", "duration": 5}`: `duration` is that video's length in seconds (1–15) and is required. 15 s in total, and reference plus output seconds may not exceed 30. Reference-video seconds are billed too. The link must download the file itself; web page links such as YouTube, Bilibili, TikTok or cloud-drive share pages are refused with a 400 at submit. |
| audio_reference | array | No | Reference audio, up to 5, public http(s) URLs only; must come with a reference image or video. Not billed. The link must download the file itself; web page links such as YouTube, Bilibili, TikTok or cloud-drive share pages are refused with a 400 at submit. |
| audio | boolean | No | The soundtrack is decided by the model and has no switch; `false` is refused (400), so leave it out. |
| prompt_extend | boolean | No | Whether the model may expand your prompt; when omitted, the model decides. |
| callback_url | string | No | Where to push the result when the job ends; see Webhooks below. |
| n | integer | No | Each job makes one clip; leave it out. A value above 1 still makes one clip, but n clips are held at submit and the extra is returned when the job ends. |

Other fields (`seed`, `negative_prompt`, `fps`, `watermark` and the like) are not passed on and do not change the price; results are always MP4, returned as a `url`.

## Price

Billed per second: cost = the per-second rate of the chosen resolution × (output seconds + the seconds of every reference video). Reference images and audio are free. Held at submit, refunded in full on failure. 1 credit = $0.005.

| Resolution | Credits / s | USD / s | A 5-second clip |
| --- | --- | --- | --- |
| 480P Not open yet | 10.8 | $0.054 | $0.27 |
| 720P Not open yet | 22.4 | $0.112 | $0.56 |
| 1080P Not open yet | 40 | $0.20 | $1.00 |

The 480P / 720P / 1080P tier is not open yet and is refused with 400 at submit; this table updates by itself once it opens.

## Reference material

- One endpoint, the parameters pick the mode: no image is text-to-video; one image is the first frame; two images with `image_role: first_frame` are first + last frame (first, then last); otherwise two or more are references, up to 10.

- Up to 5 reference videos (15 s in total, each with its `duration`) are used to restyle or continue that footage; with reference videos, images can only be references, not frames. Up to 5 reference audio clips, public URLs only.

- With a first frame, `size: adaptive` makes the clip follow the image. Human-face review restriction: avoid reference material containing real human faces; realistic faces can also trigger detection. `FaceDetectionSuspect` means a possible real human face was detected. `DataInspectionFailed` means the input material did not pass content review, not that the service is unavailable. Replace the reference before resubmitting rather than repeatedly retrying unchanged input; contact support about suspected false positives. Failed tasks are not billed.

- Each image is a public image URL, a `data:` URI or base64, up to 16 MB; send several as an array in `images`. We store the images before the job is queued; one that cannot be read is refused with 400, `message` says which, and nothing is charged.

- Reference files we store are kept for 7 days; results for 15 days.

**First + last frame: a transition between two images**

```
curl https://nezhagate.com/v1/videos/generations \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "wan3.0-video-prime", "prompt": "Dawn turns into dusk, clouds drifting, the light growing warmer", "images": ["https://example.com/first.png", "https://example.com/last.png"], "image_role": "first_frame", "duration": 5, "resolution": "720P", "size": "adaptive"}'
```

`image_role: first_frame` makes the two images the first and last frame; `size: adaptive` makes the clip follow the first image. Without `image_role`, two images are references.

## Job status and fields

`GET /v1/videos/jobs/{id}` reports these statuses; every field is described in [Async jobs](https://nezhagate.com/en/docs/guide/async-jobs#fields). Poll every 5–10 seconds, or let a webhook (below) push the result. Polling is free, and any key of the same account can read the job.

| Status | Meaning |
| --- | --- |
| queued | Waiting in line. Carries `queue_position` (jobs ahead of yours) and `eta_seconds` (expected wait). |
| processing | Rendering. |
| succeeded | Done. The result is in `data[0].url`, the billing detail in `usage`. |
| failed | Failed. The reason is in `error`; the credits held for the job are refunded in full. |

- `processing` can go back to `queued`, for example while every line is busy, while a finished clip is fetched again, or after a restart. If a line cannot finish a job, the gateway may hand it to the next line to render again; however many lines it passes through, a job is charged once.

- One account runs up to 50 jobs at a time; further jobs wait in submission order, and a job that has not started after 15 minutes fails and is refunded in full. Job records are kept for 3 days and video links for 15 days.

## How long it takes

Usually 1–3 minutes, faster than the standard Wan 3.0.

## Errors and refunds

An error at submit comes back at once as HTTP 4xx / 5xx: no job is created and nothing is charged. A failure after the job was accepted is reported by the job endpoint, and the hold is refunded in full. Every code is listed under [Error codes](https://nezhagate.com/en/docs/guide/errors).

### Errors at submit

| HTTP | code | Cause | What to do |
| --- | --- | --- | --- |
| 400 | invalid_request | A parameter is not valid: the duration, resolution, ratio or number of references is outside this model's range, the prompt is too long, a reference image or video cannot be read, `audio: false` was sent to a model whose clips always have sound, and so on. `message` says which. | Fix what `message` says and submit again. |
| 400 | invalid_callback_url | `callback_url` is not a public http(s) address, or is longer than 2000 characters. | Use an address that can be reached from the internet. |
| 400 | model_not_found | The model id is misspelled, `model` is missing, or the model cannot be used on this endpoint (a chat model on the image endpoint, for example). | Use a model id from `GET /v1/models` or the [API reference](https://nezhagate.com/en/docs/api). |
| 401 | invalid_api_key | The key does not exist, was deleted or is disabled, or the account is disabled. | Check the key on the API Keys page of the console. |
| 402 | insufficient_quota | Not enough balance to hold this request. The gateway holds the highest possible cost of the request first, so a burst of concurrent calls can trigger it too. | After a burst, wait a few seconds and retry; otherwise top up. |
| 429 | rate_limit_exceeded | Over the requests-per-minute limit set on this key (counted over the last 60 seconds). No `Retry-After` header. | Wait a few seconds and retry, or raise the key's limit in the console. |
| 503 | model_maintenance | The model is under maintenance and takes no requests; no other model answers in its place meanwhile. | Retry when maintenance ends; see [Status](https://nezhagate.com/en/status). |
| 503 | service_unavailable | A temporary failure on our side, such as storing a reference file. No job was created and nothing was charged. | Retry shortly. |

### Failed jobs (`status` `failed`)

| code | When you see it | What to do |
| --- | --- | --- |
| content_policy | Wan Standard and Prime: input material did not pass review. `error.reason` is `FaceDetectionSuspect` (possible real human face) or `DataInspectionFailed` (input content review failed). `error.message` explains the cause and recommended change. Failed tasks are not billed. | Rewrite the prompt or replace the reference before you submit again; the same input is refused again. |
| invalid_request | The model provider refused the request: a parameter or reference cannot be used, or the content did not pass review. `message` says why. | Adjust the parameters as `message` says and submit again. |
| upstream_error | Any other failure: every line was tried, the job waited 15 minutes without starting, the upstream refused without a reason, and so on; `message` says which. | Submitting again usually works. |

Branch on `code`, not on the text of `message`. Treat any `code` you do not recognise (or a missing one) as `upstream_error`: resubmitting usually works. Every failed job is refunded in full.

## Webhooks instead of polling

Add `callback_url` when you submit and the moment the job ends, success or failure, the gateway pushes exactly what the poll endpoint returns to that URL, signed and retried on failure. You can also set an account-wide address under Settings → Webhook in the console. [Webhook docs →](https://nezhagate.com/en/docs/webhooks)

```
curl https://nezhagate.com/v1/videos/generations \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "wan3.0-video-prime", "prompt": "A shiba inu chases falling cherry blossoms under a tree, bright sunlight, slow motion", "duration": 5, "resolution": "720P", "size": "16:9", "callback_url": "https://your-domain.com/webhook"}'
```
