# Wan 3.0

> Wan 3.0 all-in-one — text, image/video/audio references, and start/end frames with adaptive ratio, intelligent duration, and audio.

## Overview

- **Model ID**: `wan-3.0-video`
- **Workflow**: `wan/v3/video`
- **Provider**: Wan
- **Origin**: Partner Model
- **Category**: Text → Video (video)
- **Output**: A video URL.
- **Tags**: image input, video input, audio, start/end frame, 1080p, adaptive ratio
- **Unified endpoint**: `POST https://api.picsart.com/workflows/v1/models/execute` (model id in the body)
- **Compatibility endpoint**: `POST https://api.picsart.com/workflows/wan/v3/video/execute` (vendor's own parameters)
- **Execution**: long-running — use the async submit/poll pair or the SDK, which polls for you
- **Model page**: https://picsart.com/api-platform/models/wan-3.0-video

## Pricing

Rates are in USD. Every row below is an exact rate for the settings it names — the columns are the settings that change the price for this model. Every response reports what was actually charged in `response.usage.credits`.

| Tier | Rate |
| --- | --- |
| 480p | $0.05 / sec |
| 720p | $0.10 / sec |
| 1080p | $0.20 / sec |

Published rates. Volume pricing, invoicing and annual agreements: https://picsart.com/api-platform/contact-sales

## Authentication

The SDK and the REST API authenticate with a Picsart API key sent as a bearer token. Create a key in the console at https://picsart.com/api-platform/dashboard/api-keys, keep it in a server-side environment variable, and never ship it in browser code or commit it.

```bash
export PICSART_API_KEY="YOUR_KEY"
```

```http
Authorization: Bearer $PICSART_API_KEY
```

The CLI and the MCP server do not use an API key: `gen-ai login` signs in through the browser once per machine and stores a token locally.

## Interfaces

- **SDK (TypeScript)** — `@picsart/ai-sdk` (Node 20+, ESM). Type-safe `ai.generate()` / `ai.generateText()` with per-model parameter types; polls long jobs for you.
- **SDK (Python)** — `picsart-ai-sdk` (Python 3.10+, async). The same surface in snake_case: `await ai.generate()` / `await ai.generate_text()`, with a typed input per model.
- **REST** — the API gateway at `https://api.picsart.com`. Sync `/execute`, or async `/submit` + `/{taskId}/result`.
- **CLI** — `@picsart/gen-ai` (`gen-ai`). Generate from a terminal, quote cost before spending, emit JSON for pipelines.
- **MCP** — the same models inside Claude Code, Cursor, Windsurf and other MCP hosts. Setup: https://picsart.com/gen-ai-mcp/

```bash
npm install @picsart/ai-sdk      # SDK (TypeScript)
pip install picsart-ai-sdk       # SDK (Python)
npm install -g @picsart/gen-ai   # CLI (also drives the MCP server)
```

## Unified API (recommended)

One parameter set, used by the SDK and by REST alike: the names below are what `ai.generate('wan-3.0-video', …)` takes, and what goes inside the inner `params` of a REST call. Over REST the model id travels in the body, not the path — one workflow, `v1/models`, fronts the whole catalogue.

### Execution modes

- **Sync** — `POST /workflows/v1/models/execute` returns the result in one call. Only for jobs that finish within ~20 seconds; otherwise it times out.
- **Async** — `POST /workflows/v1/models/submit` returns a task id; poll `GET /workflows/v1/models/{taskId}/result` until `response.status` is `COMPLETED` or `FAILED`. Task statuses: `ACCEPTED` · `IN_PROGRESS` · `COMPLETED` · `FAILED`.
- **SDK** — `ai.generate()` always awaits the finished result; polling is hidden.

Video jobs routinely outrun the sync limit: use async over REST, or the SDK, which polls for you.

### Input parameters

- **`prompt`** (`string`, _optional_): What to make, in plain language: the subject, style, composition and motion. With an input image or video, what to change.
  - Max length: 5000 characters
- **`duration`** (`number`, _optional_): Length of the output, in seconds.
  - Default: `5`
  - Options: `-1` (auto: the model picks the length), `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `10`, `11`, `12`, `13`, `14`, `15`, `16`, `17`, `18`, `19`, `20`, `21`, `22`, `23`, `24`, `25`, `26`, `27`, `28`, `29`, `30`
- **`resolution`** (`string`, _optional_): Output size. Higher costs more and takes longer.
  - Default: `"1080P"`
  - Options: `480P`, `720P`, `1080P`
- **`aspectRatio`** (`string`, _optional_): Shape of the output as width:height. An auto or adaptive option follows the input image.
  - Default: `"adaptive"`
  - Options: `16:9`, `9:16`, `1:1`, `4:3`, `3:4`, `adaptive` (follows the input)
- **`generateAudio`** (`boolean`, _optional_): Whether the video gets a generated soundtrack. Audio can cost more.
  - Default: `true`
- **`startFrame`** (`string`, _optional_): The first frame, as an image URL. The video starts on this image.
  - Accepts: image URL
- **`endFrame`** (`string`, _optional_): The last frame, as an image URL. The video ends on this image.
  - Accepts: image URL
- **`imageUrls`** (`list<string>`, _optional_): Input images, as URLs: frames to animate, or references for subject, style or composition.
  - Accepts: image URLs (max 10)
- **`videoUrls`** (`list<string>`, _optional_): Input videos, as URLs.
  - Accepts: video URLs (max 5), up to 15s
- **`audioUrls`** (`list<string>`, _optional_): Input audio files, as URLs.
  - Accepts: audio URLs (max 5), up to 15s
- **`enableThinking`** (`boolean`, _optional_): Whether the model reasons before answering.
  - Default: `false`
- **`watermark`** (`boolean`, _optional_): Whether the result carries the vendor's AI watermark.
  - Default: `false`
- **`seed`** (`number`, _optional_): Seed for the random generator. The same seed and inputs repeat a result; leave it out for a random one.
  - Range: 0–2147483647 (step 1)

### Input JSON Schema

The inner `params` object. Over REST it is wrapped as `{ "params": { "model": "<id>", "params": <this> } }`; the SDK takes it directly.

```json
{
  "type": "object",
  "properties": {
    "prompt": {
      "type": "string",
      "maxLength": 5000,
      "description": "What to make, in plain language: the subject, style, composition and motion. With an input image or video, what to change."
    },
    "duration": {
      "type": "number",
      "enum": [
        -1,
        2,
        3,
        4,
        5,
        6,
        7,
        8,
        9,
        10,
        11,
        12,
        13,
        14,
        15,
        16,
        17,
        18,
        19,
        20,
        21,
        22,
        23,
        24,
        25,
        26,
        27,
        28,
        29,
        30
      ],
      "default": 5,
      "description": "Length of the output, in seconds."
    },
    "resolution": {
      "type": "string",
      "enum": [
        "480P",
        "720P",
        "1080P"
      ],
      "default": "1080P",
      "description": "Output size. Higher costs more and takes longer."
    },
    "aspectRatio": {
      "type": "string",
      "enum": [
        "16:9",
        "9:16",
        "1:1",
        "4:3",
        "3:4",
        "adaptive"
      ],
      "default": "adaptive",
      "description": "Shape of the output as width:height. An auto or adaptive option follows the input image."
    },
    "generateAudio": {
      "type": "boolean",
      "default": true,
      "description": "Whether the video gets a generated soundtrack. Audio can cost more."
    },
    "startFrame": {
      "type": "string",
      "format": "uri",
      "description": "The first frame, as an image URL. The video starts on this image."
    },
    "endFrame": {
      "type": "string",
      "format": "uri",
      "description": "The last frame, as an image URL. The video ends on this image."
    },
    "imageUrls": {
      "type": "array",
      "items": {
        "type": "string",
        "format": "uri",
        "description": "image URL"
      },
      "maxItems": 10,
      "description": "Input images, as URLs: frames to animate, or references for subject, style or composition."
    },
    "videoUrls": {
      "type": "array",
      "items": {
        "type": "string",
        "format": "uri",
        "description": "video URL"
      },
      "maxItems": 5,
      "description": "Input videos, as URLs."
    },
    "audioUrls": {
      "type": "array",
      "items": {
        "type": "string",
        "format": "uri",
        "description": "audio URL"
      },
      "maxItems": 5,
      "description": "Input audio files, as URLs."
    },
    "enableThinking": {
      "type": "boolean",
      "default": false,
      "description": "Whether the model reasons before answering."
    },
    "watermark": {
      "type": "boolean",
      "default": false,
      "description": "Whether the result carries the vendor's AI watermark."
    },
    "seed": {
      "type": "number",
      "minimum": 0,
      "maximum": 2147483647,
      "multipleOf": 1,
      "description": "Seed for the random generator. The same seed and inputs repeat a result; leave it out for a random one."
    }
  },
  "additionalProperties": false
}
```

### Required parameters example

The required parameters plus the defaults that shape the output, as a REST body:

```json
{
  "params": {
    "model": "wan-3.0-video",
    "params": {
      "prompt": "A serene mountain lake at golden hour, ultra detailed",
      "duration": 5,
      "resolution": "1080P",
      "aspectRatio": "adaptive"
    }
  }
}
```

### Response

The body is the gateway's status envelope: the output is at `response.result`, next to the task id, its status and the credits charged. Asset models return `url` plus an `items` array of results.

```json
{
  "status": "success",
  "response": {
    "id": "task_abc123",
    "status": "COMPLETED",
    "result": {
      "url": "https://cdn.picsart.com/…/result",
      "items": [
        {
          "url": "https://cdn.picsart.com/…/result"
        }
      ],
      "model": "wan-3.0-video"
    }
  }
}
```

What `ai.generate()` / `ai.generateText()` resolve to (the envelope already unwrapped):

```json
{
  "items": [
    {
      "url": "https://cdn.picsart.com/…/result"
    }
  ],
  "model": "wan-3.0-video"
}
```

### Usage examples

#### TypeScript (SDK)

```typescript
import { createClient } from '@picsart/ai-sdk';

const ai = createClient({
  apiKey: process.env.PICSART_API_KEY,
  apiUrl: 'https://api.picsart.com',
});

const result = await ai.generate('wan-3.0-video', {
  prompt: "A serene mountain lake at golden hour, ultra detailed",
  duration: 5,
  resolution: "1080P",
  aspectRatio: "adaptive"
});

console.log(result.url); // video URL
```

#### Python (SDK)

```python
import asyncio
import os

from picsart_ai_sdk import create_client


async def main() -> None:
    async with create_client(
        api_key=os.environ["PICSART_API_KEY"],
        api_url="https://api.picsart.com",
    ) as ai:
        result = await ai.generate("wan-3.0-video", {
            "prompt": "A serene mountain lake at golden hour, ultra detailed",
            "duration": 5,
            "resolution": "1080P",
            "aspectRatio": "adaptive",
        })

        print(result.url)  # video URL


asyncio.run(main())
```

#### cURL (sync)

```bash
curl -X POST 'https://api.picsart.com/workflows/v1/models/execute' \
  -H 'Authorization: Bearer $PICSART_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "params": {
    "model": "wan-3.0-video",
    "params": {
      "prompt": "A serene mountain lake at golden hour, ultra detailed",
      "duration": 5,
      "resolution": "1080P",
      "aspectRatio": "adaptive"
    }
  }
}'
```

#### cURL (async: submit, then poll)

```bash
# 1. Submit the job
curl -X POST 'https://api.picsart.com/workflows/v1/models/submit' \
  -H 'Authorization: Bearer $PICSART_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "params": {
    "model": "wan-3.0-video",
    "params": {
      "prompt": "A serene mountain lake at golden hour, ultra detailed",
      "duration": 5,
      "resolution": "1080P",
      "aspectRatio": "adaptive"
    }
  }
}'
# → { "status": "success", "response": { "id": "task_abc123" } }

# 2. Poll until response.status is COMPLETED
curl 'https://api.picsart.com/workflows/v1/models/task_abc123/result' \
  -H 'Authorization: Bearer $PICSART_API_KEY'
```

#### CLI

```bash
gen-ai login  # once per machine
gen-ai models info wan-3.0-video  # this model's parameters
gen-ai pricing wan-3.0-video  # quote before spending credits
gen-ai generate -m wan-3.0-video \
  -p "A serene mountain lake at golden hour" \
  -d 5 \
  --ar adaptive
```

## Compatibility API (vendor parameters)

Alongside the unified API, the `wan/v3/video` workflow takes Wan's own parameters exactly as the vendor defines them — nothing renamed or reshaped. It is the shortest path onto Picsart for request bodies already written against the vendor; the trade is that a request is written for one vendor, and the result comes back in the vendor's own shape. Same authentication.

- **Endpoint**: `POST https://api.picsart.com/workflows/wan/v3/video/execute` (sync) · `POST …/submit` + `GET …/{taskId}/result` (async)
- **SDK**: `ai.apis.run('wan/v3/video', params, { mode })` in TypeScript, `ai.apis.run("wan/v3/video", params, mode=…)` in Python — params forwarded verbatim

### Vendor parameters

| Parameter | Type | Required | Default | Details |
| --- | --- | --- | --- | --- |
| `prompt` | `string` | no | — | Text prompt describing the desired video, actions and dialogue. Use "Image 1", "Video 1" etc. to reference media by order in the media array. Chinese and English supported. Max 20,000 chars (Chinese/English combined; the vendor truncates beyond that). Either prompt or media is required. |
| `media` | `WanV3MediaItem[]` | no | — | Rich-media inputs, each { type, url }. Limits: up to 10 images; up to 5 videos (each 1-15s, <=15s total); up to 5 audio files (each 1-15s, <=15s total); at most one first_frame, one last_frame, one file and one link. Either prompt or media is required. |
| └ `type` | `string` | yes | — | Media asset type. reference_image/reference_video/reference_audio: all-in-one reference mode. first_frame (+ optional last_frame): strict start-end frame mode. file/link: document or web page, only when enable_thinking is true. Reference and frame types are mutually exclusive within one request. · Options: `reference_image`, `reference_video`, `reference_audio`, `first_frame`, `last_frame`, `file`, `link` |
| └ `url` | `string` | yes | — | URL of the media asset. For image/video/audio an HTTP(S) URL; for link a public web page URL. |
| `model` | `string` | no | `wan3.0-video` | Model name. Deployed in the International (Singapore) region. · Options: `wan3.0-video`, `wan3.0-video-prime` |
| `resolution` | `string` | no | `1080P` | Resolution tier controlling video clarity. Default: 1080P. Affects cost. · Options: `480P`, `720P`, `1080P` |
| `ratio` | `string` | no | `adaptive` | Aspect ratio of the generated video. Default: adaptive — the model chooses from the intent and input media. Ignored in strict first_frame/last_frame mode. · Options: `16:9`, `9:16`, `1:1`, `4:3`, `3:4`, `adaptive` |
| `duration` | `number` | no | — | Video duration in seconds: an integer 2-30, or -1 for intelligent duration (the model chooses). 0 and 1 are not allowed. Default: 5. With video input, input + output combined must not exceed 30s. Affects cost. |
| `audio` | `boolean` | no | `true` | Include an audio track in the output. Default: true. Pricing is unaffected. |
| `enable_thinking` | `boolean` | no | `false` | Enable deep thinking mode. Required to process file/link inputs and to parse complex image content. Default: false. Not recommended without documents or links. |
| `watermark` | `boolean` | no | `false` | Add "AI Generated" watermark in the lower-right corner. Default: false. |
| `seed` | `number` | no | — | Random seed for reproducibility (0-2147483647). If omitted, a random seed is used. Identical seeds do not guarantee identical results. |
| `options` | `object` | no | — | GenAI options for safety checks and Drive saving. |
| └ `safety_checks` | `object` | no | — | Safety check settings |
| └   `enabled` | `boolean` | no | — | Whether to run content moderation. Defaults to true. |
| └ `drive` | `object` | no | — | Save result to Picsart Drive |
| └   `name` | `string` | yes | — | File name in Picsart Drive |
| └   `attributes` | `object` | no | — | Custom attributes to attach to the file |
| └   `folder` | `object` | no | — | Target folder in Picsart Drive |
| └ `inputs_transformation` | `object` | no | — | Input transformation settings |
| └   `downscale_oversized_images` | `boolean` | no | — | Whether to downscale oversized input images. Defaults to false. |

### Example request body

```json
{
  "params": {
    "prompt": "A serene mountain lake at golden hour, ultra detailed"
  }
}
```

### Usage examples

#### TypeScript (`ai.apis.run`)

```typescript
import { createClient, ApiRunMode } from '@picsart/ai-sdk';

const ai = createClient({
  apiKey: process.env.PICSART_API_KEY,
  apiUrl: 'https://api.picsart.com',
});

// Calls the 'wan/v3/video' workflow directly — params are sent as-is.
const { result, usage } = await ai.apis.run('wan/v3/video', {
  prompt: "A serene mountain lake at golden hour, ultra detailed"
}, {
  mode: ApiRunMode.SYNC,
});

console.log(result); // workflow-specific output
console.log(usage?.credits); // amount charged, in $
```

#### Python (`ai.apis.run`)

```python
import asyncio
import os

from picsart_ai_sdk import ApiRunMode, create_client


async def main() -> None:
    async with create_client(
        api_key=os.environ["PICSART_API_KEY"],
        api_url="https://api.picsart.com",
    ) as ai:
        # Calls the 'wan/v3/video' workflow directly — params are sent as-is.
        response = await ai.apis.run("wan/v3/video", {
            "prompt": "A serene mountain lake at golden hour, ultra detailed",
        }, mode=ApiRunMode.SYNC)

        print(response["result"])  # workflow-specific output
        print((response.get("usage") or {}).get("credits"))  # amount charged, in $


asyncio.run(main())
```

#### cURL (sync)

```bash
curl -X POST 'https://api.picsart.com/workflows/wan/v3/video/execute' \
  -H 'Authorization: Bearer $PICSART_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "params": {
    "prompt": "A serene mountain lake at golden hour, ultra detailed"
  }
}'
```

#### cURL (async: submit, then poll)

```bash
# 1. Submit the job
curl -X POST 'https://api.picsart.com/workflows/wan/v3/video/submit' \
  -H 'Authorization: Bearer $PICSART_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "params": {
    "prompt": "A serene mountain lake at golden hour, ultra detailed"
  }
}'
# → { "status": "success", "response": { "id": "wf_abc123" } }

# 2. Poll until response.status is COMPLETED
curl 'https://api.picsart.com/workflows/wan/v3/video/wf_abc123/result' \
  -H 'Authorization: Bearer $PICSART_API_KEY'
```

### Response

Over HTTP the vendor-shaped result arrives inside the gateway envelope at `response.result`:

```json
{
  "status": "success",
  "response": {
    "id": "wf_abc123",
    "status": "COMPLETED",
    "result": {
      "url": "https://cdn.picsart.com/…/result.mp4",
      "mimeType": "video/mp4",
      "driveFile": {
        "id": "wf_abc123"
      }
    }
  }
}
```

`ai.apis.run()` unwraps that envelope and resolves to `{ result, usage }`:

```json
{
  "result": {
    "url": "https://cdn.picsart.com/…/result.mp4",
    "mimeType": "video/mp4",
    "driveFile": {
      "id": "wf_abc123"
    }
  }
}
```

## Additional resources

- Model page (playground): https://picsart.com/api-platform/models/wan-3.0-video
- SDK and REST API (unified): https://picsart.com/api-platform/models/wan-3.0-video#api
- Compatibility API (vendor parameters): https://picsart.com/api-platform/models/wan-3.0-video#advanced
- Pricing: https://picsart.com/api-platform/models/wan-3.0-video#pricing
- This document: https://picsart.com/api-platform/models/wan-3.0-video/llms.txt
- All models: https://picsart.com/api-platform/models
- Docs — quickstart https://picsart.com/api-platform/docs/quickstart, authentication https://picsart.com/api-platform/docs/authentication, SDK https://picsart.com/api-platform/docs/sdk, REST https://picsart.com/api-platform/docs/api-reference, CLI https://picsart.com/api-platform/docs/cli, MCP https://picsart.com/api-platform/docs/mcp
- CLI reference: https://picsart.github.io/picsart-mcp-cli-docs/guide/introduction
