LTX-2.3 unified text-to-video / image-to-video: prompt only → text-to-video; add one reference image → image-to-video (auto-detected). 480p / 720p / 1080p, 16:9 / 9:16. Text-to-video 5-15s, image-to-video 5-20s. Billed per second.
There is no separate image-to-video model name. Which path runs is decided by whether the request carries an image field — nothing else to declare.
| Mode | How it triggers | Duration |
|---|---|---|
| Text-to-video | Send prompt only, no image | 5-15s |
| Image-to-video | Include one reference image URL | 5-20s |
| Name | API name | Duration | Price |
|---|---|---|---|
| LTX-2.3 Video | ltx-2.3 | 5-20s | 480p $0.02/s · 720p $0.04/s · 1080p $0.045/s |
T2V + I2V (auto-switch on image)
POST /api/v1/video/generations — async: create, then poll the same endpoint, same as the other video models.
# Text-to-video (no image). Add "image" to switch to image-to-video.
curl -X POST https://apimodels.app/api/v1/video/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "ltx-2.3",
"prompt": "a drone shot flying fast over a neon city at night, cinematic",
"resolution": "720p",
"aspect_ratio": "16:9",
"duration": 5
}'
# Image-to-video: add an image URL (duration up to 20s)
# "image": "https://your-domain.com/ref.png"
# Poll status
curl "https://apimodels.app/api/v1/video/generations?task_id=TASK_ID" \
-H "Authorization: Bearer YOUR_API_KEY"Poll until state turns "completed" — data.resultUrls[0] is the finished clip. "failed" is terminal and will not change.
| Field | Required | Type | Description |
|---|---|---|---|
| model | Yes | string | "ltx-2.3" |
| prompt | Yes | string | Scene / motion / camera description |
| image | No | string | Reference image URL. If present → image-to-video; otherwise text-to-video. |
| resolution | No | string | 480p / 720p / 1080p (default 720p) |
| aspect_ratio | No | string | 16:9 / 9:16 (default 16:9) |
| duration | No | number | Seconds: text-to-video 5-15, image-to-video 5-20 |
| callback_url | No | string | URL del webhook que se invoca cuando la tarea finaliza |
Billed = the per-second rate for the resolution × the duration. Resolution is the only price tier — text-to-video and image-to-video cost the same.
| Resolution | Per second | 5s | 10s | 20s (I2V only) |
|---|---|---|---|---|
| 480p | $0.02 | $0.10 | $0.20 | $0.40 |
| 720p (default) | $0.04 | $0.20 | $0.40 | $0.80 |
| 1080p | $0.045 | $0.225 | $0.45 | $0.90 |
The totals above are just the per-second rate multiplied out. 1080p is only $0.005/s above 720p — five cents on a 10-second clip — so there is little reason to stay at 720p when you want the detail.
Per second, by resolution: 480p $0.02/s, 720p $0.04/s, 1080p $0.045/s on apimodels.app. The bill is that rate multiplied by the duration you asked for, so a 5-second 720p clip is $0.20 and a 10-second 1080p clip is $0.45. Text-to-video and image-to-video cost the same — resolution is the only price tier — and you are charged only when the job succeeds.
Add an image field with one reference image URL to the same request. There is no separate image-to-video model name: model stays "ltx-2.3", and the mode is auto-detected from whether image is present. Omit it and the same call is text-to-video. It takes a single reference image, not an array.
The ceiling depends on the mode: text-to-video runs 5 to 15 seconds, image-to-video runs 5 to 20 seconds. Set it with the duration field in seconds; leaving it out uses the model default. Because billing is per second, a longer clip costs proportionally more — 20 seconds at 1080p is $0.90.
Resolutions are 480p, 720p and 1080p (720p is the default), and aspect ratio is 16:9 or 9:16 (16:9 by default) — there is no 1:1 or 4:3. Since 1080p is only $0.005 per second above 720p, the difference on a 10-second clip is five cents, so there is little reason to stay at 720p when you want the detail.
It is async, like every other video model on apimodels.app: POST /api/v1/video/generations returns a taskId, then you poll GET /api/v1/video/generations?task_id=... until state becomes "completed" and read data.resultUrls[0]. "failed" is terminal. You can pass callback_url instead and we call your webhook when the task finishes, which skips polling entirely. Result files are deleted after 7 days, so download or re-host anything you need to keep.