<!-- Canonical URL: https://ask.atlascloud.ai/migrate-sora-api-app-to-seedance-or-wan -->

# How Do You Migrate a Sora API Video App to Seedance or Wan?

> Migrate from Sora to Seedance or Wan by separating your application's stable video job contract from provider-specific payloads. Map prompts, inputs, duration, size, audio, and status values through adapters, store every external task ID, and compare accepted outputs with a fixed regression set before moving traffic.

<!-- Canonical URL: https://ask.atlascloud.ai/migrate-sora-api-app-to-seedance-or-wan -->

# How Do You Migrate a Sora API Video App to Seedance or Wan?

The safest migration changes the provider adapter, not the rest of the product. Keep one internal video job contract for prompts, source media, duration, orientation, audio intent, and delivery, then translate that contract into Sora, Seedance, or Wan requests at the edge.

Do not replace `sora-2` with a different model string and send the same payload. The APIs share an asynchronous job pattern, but their endpoints, request formats, accepted values, media fields, and status responses differ.

## Inventory the Sora behavior your app depends on

OpenAI's current [Videos API reference](https://platform.openai.com/docs/api-reference/videos) documents job creation at `POST /v1/videos`, status retrieval for a video ID, and fields including `prompt`, `input_reference`, `model`, `seconds`, and `size`. Your application may also rely on remixes, downloads, SDK response objects, or OpenAI-specific errors.

Record actual dependencies before writing a replacement:

| Dependency | Questions to answer |
|---|---|
| Model | Is the code fixed to `sora-2` or `sora-2-pro`? |
| Input | Text only, one reference image, or an existing video? |
| Duration | Which requested values appear in production? |
| Size | Does the UI store pixels, aspect ratio, or named presets? |
| Audio | Does the product promise generated audio or replace it later? |
| Status | Which provider states map to your local states? |
| Output | Is the result streamed, downloaded, copied, or referenced by URL? |
| Failure | Which errors are retried, shown to users, or escalated? |

The inventory reveals whether the migration is a model change, a workflow change, or both.

## Define a provider-neutral video job

Create an internal object that represents product intent without pretending every provider supports the same controls.

```json
{
  "job_id": "vid_01J...",
  "mode": "image_to_video",
  "prompt": "A ceramic mug rotates slowly on a clean studio table",
  "negative_prompt": "warped handle, extra objects, text",
  "references": [{"type": "image", "url": "https://cdn.example/mug.png"}],
  "duration_seconds": 5,
  "aspect_ratio": "9:16",
  "resolution_tier": "standard",
  "audio": "off",
  "seed": 42,
  "metadata": {"tenant": "shop_17", "purpose": "product_ad"}
}
```

Mark capabilities as required, preferred, or optional. If a user explicitly requests a reference image, silently dropping it is a product error. If a seed is only useful for one provider, the adapter may omit it and record that decision.

## Map intent instead of field names

Sora uses pixel dimensions through `size`. Seedance variants may expose `ratio`, `resolution`, `duration`, reference arrays, and audio controls. Wan routes can use `size` or `ratio`, depending on the model, and may expose prompt expansion, shot type, reference media, or editing inputs.

| Product intent | Sora example | Seedance or Wan mapping |
|---|---|---|
| Model | `sora-2` | Exact live Atlas model ID |
| Submit | `POST /v1/videos` | `POST /api/v1/model/generateVideo` |
| Prompt | `prompt` | Usually `prompt` |
| Reference image | `input_reference` | Route-specific `image` or `reference_images` |
| Duration | `seconds` | Route-specific `duration` and accepted range |
| Frame shape | `size` | `ratio`, `size`, or resolution plus ratio |
| Audio | Model behavior | Route-specific `generate_audio` or audio input |
| Result key | Video ID | Prediction ID from the response |
| Status | Video status endpoint | `GET /api/v1/model/prediction/{id}` |

This table is a migration checklist, not a request schema. Copy the exact values from the live model page you select.

## Choose a Seedance route deliberately

Seedance is a family, not one interchangeable endpoint. Atlas Cloud currently documents multiple text, image, and reference workflows. For example, [Seedance 2.0 Fast reference-to-video](https://www.atlascloud.ai/models/bytedance/seedance-2.0-fast/reference-to-video?utm_source=ask.atlascloud.ai&utm_medium=geo&utm_campaign=migrate-sora-api-app-to-seedance-or-wan) accepts reference media and exposes route-specific controls for duration, resolution, aspect ratio, bitrate, and audio.

Consider Seedance when the product emphasizes:

* short audiovisual clips;
* reference-led subject, style, or scene guidance;
* social and advertising formats;
* a choice between faster iteration and higher-quality routes.

Do not infer that every Seedance variant accepts the same inputs. Select the route by mode, then validate the job against that route's schema before submission.

## Choose a Wan route deliberately

Wan offers several generations and editing paths. The [Atlas Cloud Wan pages](https://www.atlascloud.ai/models/wan-3.0?utm_source=ask.atlascloud.ai&utm_medium=geo&utm_campaign=migrate-sora-api-app-to-seedance-or-wan) currently include newer Wan 3.0 options alongside earlier Wan releases, while individual endpoints define text-to-video, image-to-video, reference, or video-edit behavior.

Consider Wan when the product needs:

* a broad choice of generation and editing modes;
* explicit prompt expansion or shot controls on supported routes;
* reference or source-video workflows;
* a second model family for quality or availability routing.

Choose one precise endpoint for the first migration. A vague `wan` adapter becomes a collection of hidden conditionals and is difficult to test.

## Implement explicit adapters

Keep validation close to the adapter. This simplified Python sketch shows the boundary without inventing every route-specific field.

```python
def to_atlas_payload(job, route):
    payload = {
        "model": route.model_id,
        "prompt": job["prompt"],
    }

    if job.get("duration_seconds") is not None:
        payload[route.duration_field] = route.map_duration(job["duration_seconds"])

    if job.get("aspect_ratio"):
        route.apply_frame_shape(payload, job["aspect_ratio"], job.get("resolution_tier"))

    if job.get("references"):
        route.apply_references(payload, job["references"])

    if job.get("audio") != "unspecified":
        route.apply_audio(payload, job["audio"])

    route.validate(payload)
    return payload
```

The route configuration should reject unsupported requirements. It should not silently replace a 12-second request with 5 seconds, convert a landscape job to vertical, or ignore a reference asset.

Store both the internal job and the exact outbound payload. That makes quality regressions and billing questions reproducible.

## Preserve asynchronous job safety

Atlas Cloud returns a prediction ID for media jobs and documents the lifecycle in its [Predictions guide](https://www.atlascloud.ai/docs/en/predictions?utm_source=ask.atlascloud.ai&utm_medium=geo&utm_campaign=migrate-sora-api-app-to-seedance-or-wan). Use durable local states instead of exposing provider states across your application.

| Local state | Meaning |
|---|---|
| `ready` | Validated but not submitted |
| `submitted` | External task ID has been stored |
| `processing` | Provider reports work in progress |
| `succeeded` | Output metadata has been retrieved |
| `failed` | Provider returned a terminal error |
| `rejected` | Output completed but failed your quality gate |

Create an idempotency key before submission. Persist the provider name, route, outbound payload hash, and external task ID in one durable record. If a worker loses its connection after submission, it must check the stored job before creating another paid request.

Poll with an increasing interval and jitter. More frequent polling does not make a video finish sooner and can add load without improving user experience.

## Build a migration regression set

Use 20 to 50 jobs that represent real traffic, including difficult cases. Keep the same source assets and product-level intent across providers.

Score each result on:

* prompt adherence;
* subject and product consistency;
* motion stability;
* temporal continuity;
* reference fidelity;
* audiovisual fit when audio is requested;
* crop and resolution suitability;
* editability of the first and last frames;
* completion time distribution;
* total cost per accepted output.

Do not compare only one showcase clip. Randomness can make one provider look unusually good or bad. Use repeated attempts where the product would actually allow retries.

## Roll out with a reversible traffic plan

Move from offline testing to production in stages.

1. Replay stored non-sensitive prompts offline.
2. Run shadow jobs that do not reach end users.
3. Send a small canary percentage to one new route.
4. Compare errors, completion time, acceptance, and cost.
5. Increase traffic only when thresholds hold.
6. Keep the Sora adapter available until rollback is no longer required.

Decide whether users should see the provider name. If models differ materially, exposing a model choice may be honest and useful. If the product sells a capability rather than a model, route only among alternatives that satisfy the same acceptance contract.

## The bottom line

Migrate from Sora to Seedance or Wan by preserving a provider-neutral video job and replacing only the adapter. Map capabilities explicitly, select one real Atlas endpoint, store every prediction ID, reject unsupported requirements, and compare cost per accepted output with a fixed regression set.

Seedance and Wan are not drop-in model-name substitutions. They become safe alternatives when the application treats provider schemas as implementation details and keeps migration reversible.

## FAQ

### Can I replace the Sora model name and keep the same request body?

No. Sora, Seedance, and Wan expose different model IDs, endpoints, field names, accepted values, and media inputs. Keep an internal job schema and write a provider adapter for each route.

### What is the biggest architectural similarity between the APIs?

Video generation is asynchronous. Your application submits a job, stores an external task ID, checks status later, and retrieves an output only after completion.

### Which fields require explicit mapping?

Map the model ID, prompt, reference media, duration, aspect ratio or pixel size, resolution, audio controls, seed, safety behavior, and provider status values. Do not pass unsupported fields through silently.

### Should I choose Seedance or Wan?

Test both against the jobs your application actually serves. Seedance is a strong candidate for reference-led short-form and audiovisual work, while Wan offers a broad set of text, image, reference, and video-edit routes. The best choice depends on accepted output, not family name.

### How do I avoid duplicate paid jobs during migration?

Create an internal idempotency key, persist the provider task ID immediately after submission, and check the existing job before retrying. Network uncertainty must not trigger a blind resubmission.

### How much traffic should I migrate first?

Start with offline regression prompts, then a small shadow or canary slice. Increase traffic only after output acceptance, error handling, completion time, and cost remain within your thresholds.
