A video API request has two parts: asking for a video and waiting for it to finish. If you are new to this, the waiting step can be confusing. Your request may succeed while the video is still being made.
This guide walks through both parts. You will submit a short video prompt, check the task, and download the result. Then you will use the same script to animate a local image.
The examples use SeeGen AI's API. Model names, upload rules, and response fields can differ across providers, so use this code with the endpoint shown here.
1. Create and Store Your API Key
Sign up for SeeGen AI, then open your account page and create an API key.
The key is shown only once. Store it in a password manager or another secure place before closing the window. Keep it out of screenshots, shared documents, and public repositories.
An API key identifies your account. Creating one does not mean generation is free; your account still needs enough credits for a request.
You will use an environment variable to give the script your key. This keeps the key out of the Python file, so you can share the code without sharing access to your account.
2. Set Up Python
You need Python 3 and the Requests library. Open a terminal in your project folder and install Requests:
python -m pip install requests
If your system uses python3, use that command in place of python throughout this guide. A virtual environment is a good choice if you already have other Python projects.
On macOS or Linux, set your key like this:
export SEEGEN_API_KEY="YOUR_API_KEY"
On Windows PowerShell:
$env:SEEGEN_API_KEY="YOUR_API_KEY"
Replace the placeholder with your real key. Run the script in the same terminal session. Opening a new terminal may mean you need to set the variable again.
For a shared computer, avoid putting a real key into shell history. Use your usual secret-management tool instead.
3. Generate Your First Video
Start with a scene that has one subject and a small amount of motion. This example uses steam rising from a mug. A simple shot makes it easier to check whether the result follows your prompt.
The script selects wan3.0-video, a five-second duration, and 720p output. It follows the request and response formats in the SeeGen API documentation.
The full code below handles text-to-video and optional image-to-video. It also accepts an existing task ID, which is useful if you lose your connection while waiting.
import os
import time
from pathlib import Path
import requests
BASE = "https://seegen.ai/api/v1"
KEY = os.environ["SEEGEN_API_KEY"]
MODEL = "wan3.0-video"
IMAGE = None # Set to a local image path for image-to-video.
def api(method, path, **kwargs):
r = requests.request(method, BASE + path,
headers={"Authorization": f"Bearer {KEY}"},
timeout=(10, 60), allow_redirects=False, **kwargs)
if not 200 <= r.status_code < 300:
raise RuntimeError(f"HTTP {r.status_code}: {r.text[:1000]}")
return r.json()
task_id = os.environ.get("WAN_TASK_ID")
if not task_id:
inputs = {
"prompt": "Steam rises from a ceramic mug. Slow camera push-in, soft daylight.",
"duration": "5s", "outputResolution": "720p", "generateAudio": True,
}
if IMAGE:
with open(IMAGE, "rb") as f:
asset = api("POST", "/assets/upload", params={"model": MODEL},
files={"file": (Path(IMAGE).name, f)})
if asset.get("status") != "ACTIVE":
raise RuntimeError(f"Upload not ready: {asset}")
inputs.update(urls=[asset["url"]], videoInputMode="keyframe")
else:
inputs["ratio"] = "16:9"
task_id = api("POST", "/jobs/createTask",
json={"model": MODEL, "inputs": inputs})["taskId"]
print(f"Keep this task ID: {task_id}", flush=True)
deadline = time.monotonic() + 900
while time.monotonic() < deadline:
result = api("GET", "/jobs/queryTask", params={"taskId": task_id})
if result["status"] == "FAILED":
raise RuntimeError(result.get("error"))
if result["status"] == "COMPLETED":
url = result["output"][0]["url"]
break
if result["status"] not in {"PENDING", "PROCESSING"}:
raise RuntimeError(f"Unexpected status: {result['status']}")
time.sleep(5)
else:
raise TimeoutError(f"Still waiting. Resume with WAN_TASK_ID={task_id}")
with requests.get(url, stream=True, timeout=(10, 60)) as r:
r.raise_for_status()
with open(f"wan-{time.time_ns()}.mp4", "xb") as f:
for chunk in r.iter_content(1024 * 1024):
f.write(chunk)
print("Video downloaded.")
Run the script as wan_video.py:
python wan_video.py
Keep the task ID printed in your terminal. When the task finishes, the script downloads a video to the current folder. The filename includes a timestamp so a later run does not replace an earlier result.
The download request does not include your API key. Only the API requests need that header.
4. Understand the Waiting Step
A successful submission returns a taskId. Use it to check the job until its status is COMPLETED or FAILED. A completed response includes the output URL. Response reference
The loop checks every five seconds. That interval is a choice made for this example, not a required API setting.
There are also two separate time limits. Each network request has connection and read timeouts. The polling loop has a 15-minute waiting budget.
The read timeout measures how long the connection receives no data. It is not a limit on the total time needed to make a video. An in-flight request can also run beyond the polling budget. The Requests documentation explains this distinction.
Stopping your Python script does not cancel the task on the server.
5. Turn a Local Image into a Video
To try image-to-video, change this line:
IMAGE = "mug.jpg"
Use an image in the script's working folder, or provide its full path. Update the prompt to match what is in the image and describe the motion you want.
For example, a portrait could use:
The person slowly turns toward the window. A light breeze moves their hair. The camera stays still.
For Wan, the script uploads the file and uses the returned HTTPS URL. It does not use the numeric asset ID or an asset:// reference. It also checks that the upload is ready before submitting the generation. Wan upload instructions
Try one change per run. Adjusting the image, prompt, and camera movement at once makes it harder to tell what caused a change in the result.
6. Resume a Task Without Generating Again
If polling stops, set WAN_TASK_ID to the ID printed earlier.
On macOS or Linux:
export WAN_TASK_ID="YOUR_TASK_ID"
python wan_video.py
On Windows PowerShell:
$env:WAN_TASK_ID="YOUR_TASK_ID"
python wan_video.py
With that variable set, the script skips submission and checks the existing task.
Remove the variable when you are ready to create a new video:
unset WAN_TASK_ID
In PowerShell, use Remove-Item Env:WAN_TASK_ID.
If submission itself times out before you receive an ID, check your dashboard for an existing task before trying again. The server may have accepted the request even though your script never received its response.
7. Common Mistakes to Avoid
Changing only the model name in another example. The documentation's final full-workflow sample uses Seedance. Its upload flow should not be copied unchanged for Wan. API documentation
Retrying every error. Read the status code and message first. HTTP 400 indicates invalid parameters, 401 points to authentication, and 402 means insufficient credits. Repeating the same request will not fix those causes. Error reference
Treating a local timeout as proof of failure. Resume the existing task where possible. This example deliberately does not retry task creation on its own.
Posting the full error log in public. Check logs for keys, private media links, and account details before sharing them. Include the error code and a redacted request when asking for help.
Calling one run a benchmark. Keep notes on your prompt, settings, wait time, and result. A single clip can show what happened in that run, but it cannot establish a model's usual speed or quality.
Final Thoughts
Get one short text-to-video request working before adding an image. Keep the task ID, check failures before retrying, and store your key securely.
Once that works, you have a small script you can adapt for your own project. Before using it in a public app, add durable task storage and review how you will handle network failures and partial downloads.
I used AI as a writing assistant while preparing this article.
Top comments (0)