# Seedance 2.0 — Scenepond API

Model ID: `bytedance/seedance-2.0/reference-to-video`

Model developer: ByteDance

Purpose: Reference to video

Source page: https://open.scenepond.ai/en/models/bytedance/seedance-2.0/reference-to-video#api

Reviewed: 2026-10-11

Base URL: `https://open.scenepond.ai/v1`

Authenticate every request with Authorization: Bearer and your Scenepond API key. Keep the key on your server.

## Scenepond asynchronous tasks

`POST /v1/tasks`

### Minimum request

#### cURL

```bash
curl --fail-with-body -X POST "https://open.scenepond.ai/v1/tasks" \
  -H "Authorization: Bearer $SCENEPOND_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @- <<'JSON'
{
  "model": "bytedance/seedance-2.0/reference-to-video",
  "parameters": {
    "prompt": "A cat walking on a quiet beach",
    "duration": 4,
    "resolution": "720p",
    "image_urls": [
      "https://example.com/reference.png"
    ]
  }
}
JSON
```

#### Python

```python
import os
import json
from urllib.parse import quote
from urllib.request import Request, urlopen

payload = json.loads("{\n  \"model\": \"bytedance/seedance-2.0/reference-to-video\",\n  \"parameters\": {\n    \"prompt\": \"A cat walking on a quiet beach\",\n    \"duration\": 4,\n    \"resolution\": \"720p\",\n    \"image_urls\": [\n      \"https://example.com/reference.png\"\n    ]\n  }\n}")

request = Request(
    "https://open.scenepond.ai/v1/tasks",
    headers={
        "Authorization": "Bearer " + os.environ["SCENEPOND_API_KEY"],
        "Content-Type": "application/json",
    },
    data=json.dumps(payload).encode("utf-8"),
    method="POST",
)
with urlopen(request) as response:
    result = json.load(response)
print(json.dumps(result, indent=2))
```

#### JavaScript

```javascript
const response = await fetch("https://open.scenepond.ai/v1/tasks", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.SCENEPOND_API_KEY}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
  "model": "bytedance/seedance-2.0/reference-to-video",
  "parameters": {
    "prompt": "A cat walking on a quiet beach",
    "duration": 4,
    "resolution": "720p",
    "image_urls": [
      "https://example.com/reference.png"
    ]
  }
})
});
if (!response.ok) throw new Error(`API error: ${response.status} ${await response.text()}`);
const result = await response.json();
console.log(result);
```

### Request parameters

- `model` — **Required**; `string`. Use this exact public model ID.
  - Constraints: `const = "bytedance/seedance-2.0/reference-to-video"`

- `parameters` — **Required**; `object`. Generation parameters for this model.

### Fields in parameters

These fields apply when parameters is supplied. A required child does not make its optional parent required.

- `parameters.prompt` — **Optional**; `string`. Describe the subject, scene, and desired generation.

- `parameters.resolution` — **Optional**; `string`. Requested output resolution.
  - Constraints: `enum = 480p, 720p, 1080p, 4k`

- `parameters.generate_audio` — **Optional**; `boolean`. Whether audio generation is requested.

- `parameters.duration` — **Optional**; `integer`. Requested video duration in seconds.
  - Constraints: `minimum = 4`; `maximum = 15`

- `parameters.aspect_ratio` — **Optional**; `string`. Requested output width-to-height ratio.
  - Constraints: `enum = 21:9, 16:9, 4:3, 1:1, 3:4, 9:16`

- `parameters.image_urls` — **Optional**; `string[]`. Reference image URLs, in first-frame then last-frame order.
  - Constraints: `maximum items = 9`

- `parameters.video_urls` — **Optional**; `string[]`. Additional reference video URLs.
  - Constraints: `maximum items = 3`

- `parameters.audio_urls` — **Optional**; `string[]`. Reference audio URLs.
  - Constraints: `maximum items = 3`


### Illustrative responses

Response examples show the structure only. IDs, URLs, and values are placeholders.

#### Submission response

```json
{
  "task_id": "task_EXAMPLE",
  "model": "bytedance/seedance-2.0/reference-to-video",
  "status": "queued",
  "progress": 0,
  "created_at": 1700000000,
  "finished_at": 0,
  "error": null,
  "links": {
    "self": "/v1/tasks/task_EXAMPLE",
    "artifacts": "/v1/tasks/task_EXAMPLE/artifacts"
  }
}
```

#### Completed task status

```json
{
  "task_id": "task_EXAMPLE",
  "model": "bytedance/seedance-2.0/reference-to-video",
  "status": "succeeded",
  "progress": 100,
  "created_at": 1700000000,
  "finished_at": 1700000060,
  "error": null,
  "links": {
    "self": "/v1/tasks/task_EXAMPLE",
    "artifacts": "/v1/tasks/task_EXAMPLE/artifacts"
  }
}
```

#### Artifact listing

```json
{
  "task_id": "task_EXAMPLE",
  "artifacts": [
    {
      "key": "video",
      "type": "video",
      "mime_type": "video/mp4",
      "content_url": "https://open.scenepond.ai/v1/tasks/task_EXAMPLE/artifacts/video/content",
      "expires_at": 1701209660
    }
  ]
}
```

#### Expired artifact response (API key authentication)

```json
{
  "error": {
    "message": "Artifact content has expired or is no longer available",
    "type": "artifact_expired",
    "code": "artifact_expired"
  }
}
```

### Follow-up requests

#### 2. Check task status

Poll every few seconds until succeeded or failed.

`GET /v1/tasks/{task_id}`

##### cURL

```bash
curl --fail-with-body -X GET "https://open.scenepond.ai/v1/tasks/$SCENEPOND_TASK_ID" \
  -H "Authorization: Bearer $SCENEPOND_API_KEY"
```

##### Python

```python
import os
import json
from urllib.parse import quote
from urllib.request import Request, urlopen

request = Request(
    "https://open.scenepond.ai/v1/tasks/" + quote(os.environ["SCENEPOND_TASK_ID"], safe="") + "",
    headers={
        "Authorization": "Bearer " + os.environ["SCENEPOND_API_KEY"],
        "Content-Type": "application/json",
    },
    method="GET",
)
with urlopen(request) as response:
    result = json.load(response)
print(json.dumps(result, indent=2))
```

##### JavaScript

```javascript
if (!process.env.SCENEPOND_TASK_ID) throw new Error("Set SCENEPOND_TASK_ID first");
const response = await fetch("https://open.scenepond.ai/v1/tasks/" + encodeURIComponent(process.env.SCENEPOND_TASK_ID) + "", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.SCENEPOND_API_KEY}`,
    "Content-Type": "application/json"
  }
});
if (!response.ok) throw new Error(`API error: ${response.status} ${await response.text()}`);
const result = await response.json();
console.log(result);
```

#### 3. Get the results

After succeeded, list artifacts and download each content_url with the same API key.

`GET /v1/tasks/{task_id}/artifacts`

##### cURL

```bash
curl --fail-with-body -X GET "https://open.scenepond.ai/v1/tasks/$SCENEPOND_TASK_ID/artifacts" \
  -H "Authorization: Bearer $SCENEPOND_API_KEY"
```

##### Python

```python
import os
import json
from urllib.parse import quote
from urllib.request import Request, urlopen

request = Request(
    "https://open.scenepond.ai/v1/tasks/" + quote(os.environ["SCENEPOND_TASK_ID"], safe="") + "/artifacts",
    headers={
        "Authorization": "Bearer " + os.environ["SCENEPOND_API_KEY"],
        "Content-Type": "application/json",
    },
    method="GET",
)
with urlopen(request) as response:
    result = json.load(response)
print(json.dumps(result, indent=2))
```

##### JavaScript

```javascript
if (!process.env.SCENEPOND_TASK_ID) throw new Error("Set SCENEPOND_TASK_ID first");
const response = await fetch("https://open.scenepond.ai/v1/tasks/" + encodeURIComponent(process.env.SCENEPOND_TASK_ID) + "/artifacts", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.SCENEPOND_API_KEY}`,
    "Content-Type": "application/json"
  }
});
if (!response.ok) throw new Error(`API error: ${response.status} ${await response.text()}`);
const result = await response.json();
console.log(result);
```

#### 4. Download before expiry

Set SCENEPOND_TASK_ID to task_id and SCENEPOND_ARTIFACT_KEY to artifacts[].key. Download the binary file before expires_at. The content endpoint supports GET, HEAD, and Range requests; saving content_url alone does not save the media.

`GET /v1/tasks/{task_id}/artifacts/{artifact_key}/content`

##### cURL

```bash
curl --fail-with-body --output output.bin -X GET "https://open.scenepond.ai/v1/tasks/$SCENEPOND_TASK_ID/artifacts/$SCENEPOND_ARTIFACT_KEY/content" \
  -H "Authorization: Bearer $SCENEPOND_API_KEY"
```

##### Python

```python
import os
import json
from urllib.parse import quote
from urllib.request import Request, urlopen

request = Request(
    "https://open.scenepond.ai/v1/tasks/" + quote(os.environ["SCENEPOND_TASK_ID"], safe="") + "/artifacts/" + quote(os.environ["SCENEPOND_ARTIFACT_KEY"], safe="") + "/content",
    headers={
        "Authorization": "Bearer " + os.environ["SCENEPOND_API_KEY"],
        "Content-Type": "application/json",
    },
    method="GET",
)
with urlopen(request) as response:
    with open("output.bin", "wb") as output:
        output.write(response.read())
print("Saved output.bin")
```

##### JavaScript

```javascript
import { writeFile } from "node:fs/promises";
if (!process.env.SCENEPOND_TASK_ID) throw new Error("Set SCENEPOND_TASK_ID first");
if (!process.env.SCENEPOND_ARTIFACT_KEY) throw new Error("Set SCENEPOND_ARTIFACT_KEY first");
const response = await fetch("https://open.scenepond.ai/v1/tasks/" + encodeURIComponent(process.env.SCENEPOND_TASK_ID) + "/artifacts/" + encodeURIComponent(process.env.SCENEPOND_ARTIFACT_KEY) + "/content", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.SCENEPOND_API_KEY}`,
    "Content-Type": "application/json"
  }
});
if (!response.ok) throw new Error(`API error: ${response.status} ${await response.text()}`);
await writeFile("output.bin", Buffer.from(await response.arrayBuffer()));
console.log("Saved output.bin");
```

### Request notes

- Images and videos use the same task API. Select a model; channel selection is automatic.
- Authenticate every request with Authorization: Bearer and your Scenepond API key. Keep the key on your server.
- Submit once, save task_id, then poll until succeeded or failed. Download results from the artifacts endpoint.
- Creation returns the task ID after acceptance. Some tasks may already be complete when the ID is returned.
- Accepted submissions return HTTP 202 with Location: /v1/tasks/{task_id}.
- Generated artifacts are stored for 14 days. Download them before expires_at; expired artifacts cannot be recovered.
- Do not automatically resubmit a timed-out POST. It may already have created a billable task.
- Parameters and limits vary by model. Only the fields listed for this model are accepted.
- Generated images and videos are stored for 14 days. Download and save them before expiry.
- Each stored artifact includes expires_at, its expiry time as a Unix timestamp in seconds. Use that value for each file; do not calculate expiry from the task creation time.
- Some older results may omit expires_at. This does not mean unlimited storage; download them as soon as possible.
- After expiry, content requests authenticated with an API key (without an access query parameter) return HTTP 410 with code artifact_expired. Private access links may return 404 for unavailable content. Expired files cannot be downloaded or recovered.
- Keep content_url private. Saving a link does not preserve the file; download the actual bytes to your own storage before expires_at.
- Video charges use the published estimated per-second rates.
