How to Fine-Tune Wan 2.2 With an API

A step-by-step guide to training a Wan 2.2 LoRA on fal with cURL and Python: dataset layout, the trainer endpoint and its inputs, the published per-step price, and calling the trained LoRA from the Wan 2.2 text-to-video endpoint.

Jim CaiOct 16, 20268 min read
How to Fine-Tune Wan 2.2 With an API

Zip 10 or more images or short clips with matching .txt captions, upload the zip, and send its URL to the fal-ai/wan-22-trainer/t2v-a14b endpoint. Training is billed per step at the rate on the trainer's model page, with a 100-step minimum. The trainer returns a .safetensors LoRA file, which you pass in the loras array of fal-ai/wan/v2.2-a14b/text-to-video/lora to generate video in your style through the same queue API.

Wan 2.2 is an open video model, and a LoRA is the cheapest way to make it produce your style, character or product. On fal you do not need a GPU or a training script. You send a zip file to a trainer endpoint, get back a LoRA file, and pass that file to the Wan 2.2 inference endpoint. Both steps use the same queue API, so you can call them with cURL or Python.

This guide trains a text-to-video LoRA. The image-to-video trainer uses the same API flow, but requires video training data; image-only datasets are supported only by the text-to-video trainer.

What you need

  • A fal account and an API key from the keys page, exported as FAL_KEY.
  • 10 or more images or short video clips that show the style or subject you want.
  • For Python, the client library: pip install fal-client.
bash
export FAL_KEY="your-api-key"

The endpoints this guide uses

StepEndpoint IDWhat it does
Trainfal-ai/wan-22-trainer/t2v-a14bTrains a Wan 2.2 text-to-video LoRA
Trainfal-ai/wan-22-trainer/i2v-a14bTrains a Wan 2.2 image-to-video LoRA
Inferencefal-ai/wan/v2.2-a14b/text-to-video/loraGenerates video with your LoRA loaded

The trainers are listed on the model pages as "T2V 14B 480P" and "I2V 14B 480P".

Step 1: Prepare the dataset

The trainer takes one zip archive. Put your images or videos in it, and add an optional caption file for each one. A caption file is a .txt file with the same name as the media file it describes.

dataset.zip
├── 01.mp4
├── 01.txt
├── 02.mp4
├── 02.txt
├── 03.jpg
├── 03.txt
└── ...

Guidelines from the trainer's input schema, plus what usually helps:

  • Use at least 10 files. The schema asks for at least 10 images or videos, and more is better.
  • Keep the set consistent. Every file should show the style or subject you want the LoRA to learn. Mixed styles teach a mixed result.
  • Write one caption per file. Describe the subject, action, setting and camera. Captions let the model learn what is your style and what is just content.
  • Pick a trigger phrase. Use a rare token such as sks_style in every caption you supply, so you can switch the style on in prompts later. Set trigger_phrase to the same token if any captions will be auto-generated; it prefixes generated captions but does not modify captions you provide.
  • Normalize your clips. Wan 2.2 generates 81 frames at 16 fps by default. Set auto_scale_input: true to have the trainer rescale each video to 81 frames at 16 fps.

Zip the folder:

bash
cd my-dataset && zip -r ../dataset.zip . && cd ..

Step 2: Upload the dataset

The trainer reads the dataset from a URL. With Python, upload the zip to fal storage:

python
import fal_client

dataset_url = fal_client.upload_file("dataset.zip")
print(dataset_url)

With cURL, any HTTPS URL that the trainer can download works, such as a pre-signed link from your own object storage.

Step 3: Start training

The trainer accepts five inputs:

InputDefaultNotes
training_data_urlrequiredURL of the zip from step 2
number_of_steps400More steps learn more detail and cost more. Scale it with dataset size
learning_rate0.0002Higher values train faster but overfit sooner
trigger_phrase""Prefixes auto-generated captions; captions you supply are left as-is
auto_scale_inputfalseRescales videos to 81 frames at 16 fps

Training runs on the fal queue. Submit the job, then check its status until it completes.

cURL:

bash
curl -X POST "https://queue.fal.run/fal-ai/wan-22-trainer/t2v-a14b" \
  -H "Authorization: Key $FAL_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "training_data_url": "https://your-bucket.example.com/dataset.zip",
    "number_of_steps": 400,
    "learning_rate": 0.0002,
    "trigger_phrase": "sks_style",
    "auto_scale_input": true
  }'

The response contains a request_id, a status_url and a response_url. Save the two URLs as STATUS_URL and RESPONSE_URL, and use them exactly as returned, because they can differ from the path you submitted to.

bash
# Check progress (add ?logs=1 to include training logs)
curl "$STATUS_URL?logs=1" -H "Authorization: Key $FAL_KEY"

# When the status is COMPLETED, fetch the result
curl "$RESPONSE_URL" -H "Authorization: Key $FAL_KEY"

Python:

python
import fal_client


def on_update(status):
    if isinstance(status, fal_client.InProgress):
        for log in status.logs or []:
            print(log["message"])


result = fal_client.subscribe(
    "fal-ai/wan-22-trainer/t2v-a14b",
    arguments={
        "training_data_url": dataset_url,
        "number_of_steps": 400,
        "learning_rate": 0.0002,
        "trigger_phrase": "sks_style",
        "auto_scale_input": True,
    },
    with_logs=True,
    on_queue_update=on_update,
)

lora_url = result["lora_file"]["url"]
print(lora_url)

For long jobs, use fal_client.submit(...) instead and keep the request ID, or pass a webhook_url so fal calls your server when training finishes.

The result has two files:

  • lora_file: the trained LoRA weights (.safetensors).
  • config_file: the configuration for loading the LoRA on the inference endpoints.

Step 4: Know the training cost and time

The trainer bills per training step, not per GPU-hour. As published on the trainer's model page on October 15, 2026:

TrainerPrice per stepMinimum charge400 steps (default)1,000 steps
fal-ai/wan-22-trainer/t2v-a14b$0.004100 steps$1.60$4.00
fal-ai/wan-22-trainer/i2v-a14b$0.005100 steps$2.00$5.00

Because the cost is linear in number_of_steps, you can price a run before you start it. Check the model page for the current rate.

The model page does not publish a training duration. Duration depends on the step count and the size of your dataset. Watch the request status and logs to see how long your runs take, and use a webhook rather than holding a connection open.

Step 5: Call your LoRA as an API

Pass the lora_file URL in the loras array of the Wan 2.2 LoRA endpoint. Start the prompt with your trigger phrase.

Each entry in loras takes:

  • path: the URL of your .safetensors file.
  • scale: the LoRA strength. The default is 1.
  • transformer: which Wan 2.2 transformer loads the LoRA. Wan 2.2 A14B has a high-noise and a low-noise transformer, and the options are high (the endpoint default), low and both. This trainer fine-tunes only the low-noise transformer, so set this to low for the LoRA trained above.

cURL:

bash
curl -X POST "https://queue.fal.run/fal-ai/wan/v2.2-a14b/text-to-video/lora" \
  -H "Authorization: Key $FAL_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "sks_style, a lighthouse on a cliff at dusk, waves crashing below, slow dolly-in",
    "loras": [
      {
        "path": "https://v3.fal.media/files/.../your_lora.safetensors",
        "scale": 1.0,
        "transformer": "low"
      }
    ],
    "resolution": "720p",
    "aspect_ratio": "16:9",
    "num_frames": 81,
    "frames_per_second": 16
  }'

Python:

python
video = fal_client.subscribe(
    "fal-ai/wan/v2.2-a14b/text-to-video/lora",
    arguments={
        "prompt": "sks_style, a lighthouse on a cliff at dusk, waves crashing below, slow dolly-in",
        "loras": [{"path": lora_url, "scale": 1.0, "transformer": "low"}],
        "resolution": "720p",
        "aspect_ratio": "16:9",
        "num_frames": 81,
        "frames_per_second": 16,
    },
)

print(video["video"]["url"])

The response contains video (the generated file) and seed. Reuse the seed to compare the same prompt with and without your LoRA. Set scale to 0 for the "without" run so that everything else stays the same.

The LoRA endpoint is billed at $0.10 per second, as shown on its model page on October 15, 2026.

Your LoRA file is a plain URL, so one endpoint can serve any number of LoRAs. Store the URL with your user or project, and pass the right one on each request. There is nothing to deploy and no GPU to keep warm.

Tips for a better LoRA

  • Start with the defaults. Run 400 steps at a learning rate of 0.0002 first, then change one setting at a time.
  • Scale steps with the dataset. A small dataset overfits with too many steps, and a large one needs more steps to learn.
  • Compare with the same seed. Generate the same prompt and seed at scale 0 and at scale 1 to see what the LoRA changes.
  • Lower the scale if outputs look burned in. A scale between 0.6 and 0.9 often keeps the style while giving the prompt more control.

FAQ

How many images or videos do I need to fine-tune Wan?

The trainer's schema asks for at least 10 images or videos, and more is better. Consistency matters more than volume. Ten clean, on-style examples beat fifty mixed ones.

How much does it cost to fine-tune Wan 2.2 on fal?

The text-to-video trainer bills per step, with a 100-step minimum. The default 400-step run costs the per-step rate times 400. The step-4 table shows the rate published on the model page and what common step counts cost.

Can I use the LoRA with image-to-video?

Train with fal-ai/wan-22-trainer/i2v-a14b if you plan to animate still images. A LoRA learns under the conditioning it was trained with, so match the trainer to how you will run inference. The image-to-video trainer needs video clips in the dataset; it rejects image-only datasets.

Do I have to deploy anything to use my LoRA?

No. The trained LoRA is a file URL. You pass it in the loras array of the Wan 2.2 LoRA endpoint on each request, and fal loads it for you.

Build with generative media on fal

Hundreds of production-ready image, video, and audio models behind one API.