# MiniMax H3 Text to Video LoRA Trainer API

> LoRA trainer for pure text-to-video generation with audio, no keyframe conditioning

Trains a LoRA from a zip of captioned video clips in `training_data_url` for text-only video generation. First-frame conditioning is fixed at 0, so keyframe training is left to the sibling trainers. Adjustable settings include `rank`, `number_of_steps` (1 to 15000), `learning_rate`, `number_of_frames`, `frame_rate`, `resolution` and a `trigger_phrase`.

- Model id: `minimax/h3/t2v/trainer`
- Made by: MiniMax
- Category: Training
- Output: file
- Price: $0.005 / step (cheapest run $10.00 (10000 API credits))
- Run: `POST https://api.doitong.com/v1/run/minimax/h3/t2v/trainer`
- Quote: `POST https://api.doitong.com/v1/quote/minimax/h3/t2v/trainer`
- Docs page: https://doitong.com/developers/models/minimax/h3/t2v/trainer

## Pricing

Price: **$0.005 / step**. Billed per training step (`number_of_steps`).

The full example input below costs $10.00 = 10000 API credits (2000 steps × 5 API credits).

Prices are the upstream price in USD, paid from the API balance in API credits (1000 API credits = 1 USD). `POST https://api.doitong.com/v1/quote/minimax/h3/t2v/trainer` with the same JSON body returns the exact price for that input; the quote is exactly what a run charges. Invalid input is never charged and failed or canceled requests are refunded automatically.

## Input parameters

| Parameter | Canonical name | Type | Required | Default | Allowed values / range | Description |
|---|---|---|---|---|---|---|
| `training_data_url` |  | string | yes |  |  | URL for a ZIP containing training videos in .mp4, .mov, .avi, or .mkv format; at least 10 clips are recommended, and larger collections are preferable. Images cannot be training media: image-only datasets and archives mixing training images with videos are rejected, although some endpoints allow images as conditioning sidecars. Optional caption files may accompany the clips, with each text file sharing its video's base name. |
| `rank` |  | enum (integer) | no | `32` | `8`, `16`, `32`, `64`, `128` | LoRA adaptation rank, trading increased memory consumption for greater capacity as the setting rises. |
| `number_of_steps` |  | integer | no | `2000` | 1 – 15,000 | Total count of training steps. |
| `learning_rate` |  | number | no | `0.0002` | 0.000001 – 1 | Optimization learning rate, balancing a possible speedup in training against the risk of overfitting when increased. |
| `number_of_frames` |  | integer | no | `73` | 22 – 124 | Frame count for each training sample; dividing it by 17 must leave a remainder of 5. Examples include 22, 39, 56, 73, 90, 107, and 124. |
| `frame_rate` |  | integer | no | `24` | 8 – 60 | Target video frame rate, measured in frames per second. |
| `resolution` |  | enum (string) | no | `medium` | `low`, `medium`, `high` | Training resolution. Increasing the resolution raises memory requirements. |
| `aspect_ratio` |  | enum (string) | no | `16:9` | `21:9`, `16:9`, `4:3`, `1:1`, `3:4`, `9:16` | Choose the training aspect ratio from the same set available through the MiniMax H3 API. |
| `trigger_phrase` |  | string | no | `` |  | LoRA style activation phrase, inserted at the start of captions during training. |
| `first_frame_conditioning_p` |  | number | no | `0` | 0 – 0 | Keyframe conditioning must remain disabled at 0 because this route trains exclusively on text-to-video-audio. Conditioning on keyframes instead requires a first-last-frame route or an image-to-video-audio route. |
| `auto_scale_input` |  | boolean | no | `false` |  | Whether the target fps and frame count are matched through automatic video scaling; true enables this adjustment. |
| `split_input_into_scenes` |  | boolean | no | `true` |  | Scene splitting for videos exceeding a duration threshold, enabled when true. |
| `split_input_duration_threshold` |  | number | no | `30` | 1 – 60 | Scene-splitting trigger expressed as a duration in seconds: only videos longer than the threshold are split. |
| `debug_dataset` |  | boolean | no | `false` |  | Preprocessing inspection mode for checking videos, images, and captions before a full training run. Enabling it makes the trainer return the processed training dataset in a downloadable archive for manual review. |
| `strict_dataset` |  | boolean | no | `false` |  | Strict handling of preprocessing problems that would otherwise be logged and resolved using the documented fallbacks. Enabling it rejects captions needing blank prompts, missing sidecar keyframes, and videos with unreadable audio instead of substituting a blank prompt, a clip frame, or silence, respectively. |

## Example input

Required parameters only:

```json
{
  "training_data_url": "https://example.com/your-file.zip"
}
```

A fuller example:

```json
{
  "training_data_url": "https://example.com/your-file.zip"
}
```

## Run it

Set `DOITONG_API_KEY` to a key from https://doitong.com/developers/dashboard#keys. `?wait=20` returns the finished request in the same call when it is ready within 20 seconds; otherwise poll `GET /requests/{id}` (it also accepts `?wait=`), or pass `webhook_url`.

### curl

```bash
# Run the model; wait up to 20 s for the finished result in the same call
curl -X POST "https://api.doitong.com/v1/run/minimax/h3/t2v/trainer?wait=20" \
  -H "Authorization: Bearer $DOITONG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "training_data_url": "https://example.com/your-file.zip"
  }'

# Still "queued" or "running"? Poll with the id from the response:
curl "https://api.doitong.com/v1/requests/REQUEST_ID?wait=20" \
  -H "Authorization: Bearer $DOITONG_API_KEY"
```

### JavaScript

```javascript
const API = "https://api.doitong.com/v1";
const headers = {
  "Authorization": "Bearer " + process.env.DOITONG_API_KEY,
  "Content-Type": "application/json",
};

// Run the model; wait up to 20 s for the finished result in the same call
let res = await fetch(API + "/run/minimax/h3/t2v/trainer?wait=20", {
  method: "POST",
  headers,
  body: JSON.stringify({
    "training_data_url": "https://example.com/your-file.zip"
  }),
});
let request = await res.json();
if (!res.ok) throw new Error(request.error.code + ": " + request.error.message);

// Not finished yet: long-poll until it is
while (request.status === "queued" || request.status === "running") {
  res = await fetch(API + "/requests/" + request.id + "?wait=20", { headers });
  request = await res.json();
}
if (request.status !== "succeeded") throw new Error(JSON.stringify(request.error));
console.log(request.outputs.map((o) => o.url), request.cost.credits + " API credits ($" + request.cost.usd + ")");
```

### Python

```python
import os
import requests

API = "https://api.doitong.com/v1"
headers = {"Authorization": "Bearer " + os.environ["DOITONG_API_KEY"]}

payload = {
    "training_data_url": "https://example.com/your-file.zip",
}

# Run the model; wait up to 20 s for the finished result in the same call
r = requests.post(API + "/run/minimax/h3/t2v/trainer", params={"wait": 20}, json=payload, headers=headers)
request = r.json()
if not r.ok:
    raise RuntimeError(request["error"]["code"] + ": " + request["error"]["message"])

# Not finished yet: long-poll until it is
while request["status"] in ("queued", "running"):
    request = requests.get(API + "/requests/" + request["id"], params={"wait": 20}, headers=headers).json()
if request["status"] != "succeeded":
    raise RuntimeError(request["error"])
print([o["url"] for o in request["outputs"]], request["cost"]["credits"], "API credits", "$" + request["cost"]["usd"])
```

### MCP

The MCP server at `https://api.doitong.com/v1/mcp` exposes the tools `search_models`, `get_model`, `quote`, `run`, `get_request`, `cancel_request`, `list_requests`, `create_upload` and `get_account`. A `run` call for this model:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "run",
    "arguments": {
      "model_id": "minimax/h3/t2v/trainer",
      "input": {
        "training_data_url": "https://example.com/your-file.zip"
      },
      "wait_seconds": 20
    }
  }
}
```

## Response

Every call returns a request object. `status` is one of `queued`, `running`, `succeeded`, `failed`, `canceled`. `outputs` lists the generated files with URLs on our storage (kept forever, or 7 days with `?storage=temporary`; `storage` says which); `output` is the model's own result object with the same URLs.

```json
{
  "id": "req_8k2m4q7x1z9c3v5b6n0p2r4t",
  "object": "request",
  "model": "minimax/h3/t2v/trainer",
  "status": "succeeded",
  "created_at": "2026-10-20T12:00:00Z",
  "started_at": "2026-10-20T12:00:02Z",
  "completed_at": "2026-10-20T12:00:09Z",
  "queue_position": null,
  "cost": {
    "credits": 10000,
    "usd": "10.00",
    "charged": 10000,
    "refunded": 0
  },
  "input": {
    "training_data_url": "https://example.com/your-file.zip"
  },
  "output": {
    "lora_file": {
      "url": "https://content.doitong.com/dev-api/2026/10/20/req_8k2m4q7x1z9c3v5b6n0p2r4t/0-output.jpeg",
      "content_type": "image/jpeg",
      "file_name": "0-output.jpeg",
      "file_size": 1843200
    },
    "config_file": {
      "url": "https://content.doitong.com/dev-api/2026/10/20/req_8k2m4q7x1z9c3v5b6n0p2r4t/0-output.jpeg",
      "content_type": "image/jpeg",
      "file_name": "0-output.jpeg",
      "file_size": 1843200
    },
    "debug_dataset": {
      "url": "https://content.doitong.com/dev-api/2026/10/20/req_8k2m4q7x1z9c3v5b6n0p2r4t/0-output.jpeg",
      "content_type": "image/jpeg",
      "file_name": "0-output.jpeg",
      "file_size": 1843200
    }
  },
  "outputs": [
    {
      "type": "file",
      "url": "https://content.doitong.com/dev-api/2026/10/20/req_8k2m4q7x1z9c3v5b6n0p2r4t/0-output.jpeg",
      "content_type": "image/jpeg",
      "file_size": 1843200,
      "stored": true
    }
  ],
  "error": null,
  "metrics": {
    "inference_seconds": 6.8
  },
  "urls": {
    "self": "https://api.doitong.com/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t",
    "cancel": "https://api.doitong.com/v1/requests/req_8k2m4q7x1z9c3v5b6n0p2r4t/cancel"
  }
}
```

### Output fields (`output`)

| Field | Type | Description |
|---|---|---|
| `lora_file` | object {url, content_type, file_name, file_size} | URL for trained LoRA weights in .safetensors format. |
| `config_file` | object {url, content_type, file_name, file_size} | Configuration for inference endpoint setup. |
| `debug_dataset` | object {url, content_type, file_name, file_size} \| null | Available only for requests with `debug_dataset` enabled, this downloadable archive holds the training data after preprocessing. |

## Related models

- [MiniMax H3 First Last Frame LoRA Trainer](https://doitong.com/developers/models/minimax/h3/flf2v/trainer.md) (`minimax/h3/flf2v/trainer`): $0.01 / step
- [MiniMax H3 Image to Video Audio LoRA Trainer](https://doitong.com/developers/models/minimax/h3/i2v/trainer.md) (`minimax/h3/i2v/trainer`): $0.01 / step
- [MiniMax H3 Reference to Video LoRA Trainer](https://doitong.com/developers/models/minimax/h3/ref2va/trainer.md) (`minimax/h3/ref2va/trainer`): $0.015 / step
- [ERNIE-Image Trainer](https://doitong.com/developers/models/ernie-image-trainer.md) (`ernie-image-trainer`): $0.0012 / step
- [FLUX 2 \[klein\] 9b Base Trainer](https://doitong.com/developers/models/flux-2-klein-9b-base-trainer.md) (`flux-2-klein-9b-base-trainer`): $0.0043 / step
- [Flux 2 Klein 9B Base Trainer](https://doitong.com/developers/models/flux-2-klein-9b-base-trainer/edit.md) (`flux-2-klein-9b-base-trainer/edit`): $0.0139 / step
- [FLUX 2 Trainer](https://doitong.com/developers/models/flux-2-trainer.md) (`flux-2-trainer`): $0.0064 / step
- [FLUX 2 Trainer Edit](https://doitong.com/developers/models/flux-2-trainer/edit.md) (`flux-2-trainer/edit`): $0.0389 / step

## Links

- HTML page: https://doitong.com/developers/models/minimax/h3/t2v/trainer
- All models and guides for AI agents: https://doitong.com/developers/llms.txt
- OpenAPI 3.1 with this model's typed request body: https://doitong.com/developers/openapi.json?model=minimax/h3/t2v/trainer
- Errors: https://doitong.com/developers/docs/errors
- Model details as JSON: https://api.doitong.com/v1/models/minimax/h3/t2v/trainer
