HiAPI
  • Models
  • Pricing
Search

Search HiAPI models, tools, and resources.

  • Models
  • Pricing
HiAPI

One API, All AI Models

Generate images, video, and audio with leading models through one production-ready API.

Get a free API key

AI Image API

  • All image models
  • GPT Image 2
  • Nano Banana 2
  • Seedream 5.0 Pro
  • Qwen Image 2.0 Pro
  • FLUX 1.1 Pro

AI Video API

  • All video models
  • Seedance 2.5
  • FLUX.3 Video
  • Seedance 2.0
  • Veo 3.1
  • Kling 3.0

AI Audio API

  • All audio models
  • MiniMax Music 2.6
  • MiniMax Music 1.5
  • ElevenLabs v3
  • Text to music
  • Text to speech

Product

  • Model marketplace
  • Playground
  • Pricing
  • Image API Cost Calculator
  • Free GPT Image 2 Generator
  • Free Nano Banana Image Generator
  • Outfit Preview
  • Product Photo Lab

Developers

  • Documentation
  • API Reference
  • Agent Skills
  • LLM integration index
  • Blog

Company

  • About
  • Contact support
  • Terms of Service
  • Privacy Policy

© 2026 hiapi. All rights reserved.

Open source on GitHubPython SDK on PyPI
  • TL;DR
  • What you're building, and what you need
  • The request schema
  • Minimal working example: curl
  • Minimal working example: Python
  • Writing prompts that control motion
  • Production notes
  • Related reading
  • FAQ
TutorialJul 11, 2026

How to Use the hailuo 2.3 Image to Video API: curl, Python, and a Working Request

hiapihailuo-2.3Video APIImage-to-VideoTutorial

Latest models

Explore models

Contents
  • TL;DR
  • What you're building, and what you need
  • The request schema
  • Minimal working example: curl
  • Minimal working example: Python
  • Writing prompts that control motion
  • Production notes
  • Related reading
  • FAQ

Generate it with HiAPI

Choose a model, enter your prompt, and see the result.

HiAPI Blog

Related articles

HiAPI

Generate it with HiAPI

TL;DR

  • What this is: a working recipe for the hailuo-2.3/image-to-video API on hiapi — turn one still image into a short video clip: create a task, poll it, download the .mp4.
  • Endpoint: POST https://api.hiapi.ai/v1/tasks with "model": "hailuo-2.3/image-to-video", then GET /v1/tasks/<taskId> until it reaches a terminal status.
  • Required input fields (both): prompt and image_url — a single public URL to your first frame (not an array).
  • duration is a string, not a number: "6" or "10". Passing 6 as an integer is rejected with a 400.
  • The schema is strict. There are no motion_strength, camera_movement, or resolution parameters — they're rejected with a 400. Motion is controlled entirely through the prompt.

What you're building, and what you need

You have a still image — a product shot, a character render, a photo — and you want hailuo-2.3 to animate it into a video clip that starts exactly from that frame. hailuo-2.3 is known for physically plausible motion (cloth, hair, body mechanics), which makes it a strong pick for bringing static images to life.

You need two things:

  1. A hiapi API key. Create one from your dashboard. Keys look like sk-... and go in the Authorization: Bearer header.
  2. A publicly reachable URL for your first-frame image. The API fetches the image from image_url, so it must be accessible without authentication — an S3/R2/CDN link works; a localhost path or a signed URL that has expired does not.

Like every generation model on hiapi, this one runs on the async task interface: you POST a task, get a taskId back immediately, and collect the result later by polling or via callback. There is no synchronous endpoint for video — clips take a while to render, longer than typical HTTP timeouts.

The request schema

The full input schema for hailuo-2.3/image-to-video — verified against the live API:

FieldTypeRequiredNotes
promptstring✅Describes the motion, not the scene — the scene comes from your image.
image_urlstring✅One public URL, the first frame. Singular — not image_urls.
durationstringoptional"6" or "10" (seconds). Must be a string.
prompt_optimizerbooleanoptionalLets the platform expand your prompt before generation.

The schema is strict: anything not in this table is rejected. Send motion_strength, camera_movement, or resolution and you get exactly this back:

{
  "code": 400,
  "error_code": "INVALID_REQUEST",
  "message": "invalid input: <root>: additional properties 'resolution', 'camera_movement', 'motion_strength' not allowed"
}

Two consequences worth internalizing:

  • There are no motion knobs. hailuo-2.3's motion physics are steered by the prompt alone — see the prompting section below.
  • There is no resolution or aspect-ratio control on this endpoint. Don't build UI for parameters the API will 400 on.

Minimal working example: curl

Create the task:

curl -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer sk-<your-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "hailuo-2.3/image-to-video",
    "input": {
      "prompt": "The woman turns toward the camera and smiles; wind gently moves her hair; slow cinematic push-in",
      "image_url": "https://your-cdn.example.com/first-frame.jpg",
      "duration": "6",
      "prompt_optimizer": true
    }
  }'

The response (abridged) contains the task id under data.taskId:

{ "data": { "taskId": "..." } }

Poll it:

curl -s https://api.hiapi.ai/v1/tasks/<taskId> \
  -H "Authorization: Bearer sk-<your-key>"

While the clip renders, data.status reports a non-terminal state. When it flips to "success", the video URL is at data.output[0].url; if it's "fail", data.error.code and data.error.message say why.

Download the file as soon as the task succeeds. Output URLs are signed and carry an expiry (expireAt) — store the bytes on your own storage, never hotlink the task URL.

Minimal working example: Python

The same flow as a complete script — create, poll, download:

import time
import requests

API_BASE = "https://api.hiapi.ai/v1/tasks"
API_KEY = "sk-<your-key>"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}


def create_task() -> str:
    payload = {
        "model": "hailuo-2.3/image-to-video",
        "input": {
            "prompt": (
                "The cat stretches, hops off the windowsill, and walks toward "
                "the camera; soft afternoon light, subtle handheld camera sway"
            ),
            "image_url": "https://your-cdn.example.com/first-frame.jpg",
            "duration": "6",          # string, not int: "6" or "10"
            "prompt_optimizer": True,
        },
    }
    r = requests.post(API_BASE, json=payload, headers=HEADERS, timeout=30)
    task_id = (r.json().get("data") or {}).get("taskId")
    if not task_id:
        raise RuntimeError(f"create failed ({r.status_code}): {r.text[:300]}")
    return task_id


def wait_task(task_id: str, timeout_s: int = 900, poll_s: int = 10) -> dict:
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        r = requests.get(f"{API_BASE}/{task_id}", headers=HEADERS, timeout=30)
        task = r.json().get("data") or {}
        status = task.get("status")
        if status == "success":
            return task
        if status == "fail":
            err = task.get("error") or {}
            raise RuntimeError(f"task failed: {err.get('code')}: {err.get('message')}")
        time.sleep(poll_s)
    raise TimeoutError(f"task {task_id} not finished after {timeout_s}s")


def main() -> None:
    task_id = create_task()
    print("task created:", task_id)
    task = wait_task(task_id)
    url = task["output"][0]["url"]
    # The URL is signed and expires -- download immediately, store the bytes yourself.
    video = requests.get(url, timeout=120).content
    with open("hailuo-output.mp4", "wb") as f:
        f.write(video)
    print("saved hailuo-output.mp4")


if __name__ == "__main__":
    main()

Video tasks run noticeably longer than image tasks, so the script polls every 10 seconds with a generous 15-minute ceiling. Don't poll more aggressively than every ~5 seconds — it doesn't make the render faster.

Writing prompts that control motion

Since there are no motion parameters, the prompt is your only steering wheel — and for hailuo-2.3 that's enough. Three rules of thumb:

  • Describe motion, not appearance. The model already sees your image. "a beautiful woman in a red dress" wastes the prompt; "she turns her head slowly to the left as the camera pushes in" uses it.
  • Name the camera work in plain language. "slow push-in", "orbit right", "handheld sway", "static camera" — these belong in the prompt text, not in a (nonexistent) camera_movement field.
  • Let prompt_optimizer help when in doubt. With "prompt_optimizer": true the platform expands terse prompts before generation — good default for short inputs. Turn it off when you want strict adherence to carefully engineered wording.

Production notes

Prefer callbacks over polling. Attach a callback when creating the task and hiapi will POST the terminal result to your endpoint, so you hold no open loops:

{
  "model": "hailuo-2.3/image-to-video",
  "input": { "...": "..." },
  "callback": { "url": "https://your-app.example.com/hooks/hiapi", "when": "final" }
}

"when": "final" fires once, on the terminal state. Polling is fine for scripts and notebooks; callbacks are the right shape for servers — video renders are long enough that a worker blocked on polling is real money.

Handle these errors explicitly:

  • 401 with "code": "permission_denied" — bad key, or the key isn't allowed to use this model. The message includes a request_id you can send to support.
  • 400 INVALID_REQUEST — schema violations. The message is precise, e.g. duration: got number, want string or image_url: missing required field "image_url". Log it verbatim.
  • 402 on task creation — insufficient balance. Note that video bills more per task than images, so a key that still runs image tasks fine can start failing video creation first. Top up, or check your usage in the dashboard.
  • Task reaches "fail" — generation-side failure; inspect data.error. A common cause for image-to-video specifically: image_url wasn't fetchable (private bucket, expired signed URL).

Be idempotent around retries. If your process dies between create and download, a naive restart creates (and pays for) a second render. Persist the taskId as soon as create_task returns, and on restart resume polling instead of re-creating.

Related reading

  • hailuo-2.3/image-to-video model page — playground, per-second rates, and live schema.
  • hailuo-2.3-fast/image-to-video — the lower-latency tier, same task workflow.
  • hailuo-2.3/text-to-video — no source image, prompt-only generation.
  • hiapi docs — the task interface in full, including callbacks.
  • Pricing — current per-second video rates across all models.

FAQ

What durations does hailuo-2.3 image-to-video support? Two: "6" and "10" seconds, passed as strings. Any other value — or the same value as a number — returns 400 INVALID_REQUEST.

Why does "duration": 6 fail when "duration": "6" works? The schema types duration as a string enum. The API tells you exactly that: duration: got number, want string. It's the single most common 400 on this endpoint.

Can I set the output resolution or aspect ratio? No. The endpoint exposes no resolution or aspect_ratio field, and sending one is a 400 — the schema rejects unknown properties rather than ignoring them.

How do I control camera movement or motion strength? Through the prompt. There are no camera_movement or motion_strength parameters — write the camera and motion directions as plain language ("slow push-in", "she walks toward the camera").

Can I pass multiple reference images? No — image_url is a single string, the first frame. This differs from some other models on hiapi that take an image_urls array; schemas are per-model, so always check the model page.

What image formats work for the first frame? Standard web formats (JPEG/PNG) served from a publicly reachable URL. If the platform can't fetch the URL, the task fails after creation — so validate the link is publicly accessible before submitting.

How is this different from hailuo-2.3/text-to-video? Text-to-video invents the scene from your prompt alone; image-to-video pins the first frame to your image and animates from it. The task lifecycle is identical — only the input schema differs (image_url is required here, and text-to-video has no image field at all).

What does it cost? Video is billed per second of output, and rates change — check the hailuo-2.3/image-to-video model page and the pricing page for current numbers.

Latest models

View all models
  • GPT Image 2From $0.007/image
  • Nano Banana 2From $0.051/image
  • Seedream 5.0 ProFrom $0.050/image
  • Seedance 2.5From $0.121/s

Explore models

TextImageVideoAudio
Back to blog
GPT Image 2From $0.007/image
Nano Banana 2From $0.051/image
Seedream 5.0 ProFrom $0.050/image
Seedance 2.5From $0.121/s
View all models
TextChat and reasoning
ImageGenerate and edit
VideoText and image to video
AudioSpeech and music
Start generating
View model pricing
View all articles
How to Use the flux-3 API for Text-to-Video, Audio, and Continuation

How to Use the flux-3 API for Text-to-Video, Audio, and Continuation

minimax-music-3 API: curl & Python Guide

minimax-music-3 API: curl & Python Guide

How to use grok-imagine-image-2.0/image-to-image via the hiapi API: curl, Python, and a working request

How to use grok-imagine-image-2.0/image-to-image via the hiapi API: curl, Python, and a working request

How to Use grok-imagine-image-2.0/text-to-image via the hiapi API: curl, Python, and a Working Request

How to Use grok-imagine-image-2.0/text-to-image via the hiapi API: curl, Python, and a Working Request

How to Use the qwen-image-3.0 API: curl, Python, and a Working Request

How to Use the qwen-image-3.0 API: curl, Python, and a Working Request

How to Use qwen-image-3.0-pro via the hiapi API: curl, Python, and a Working Request

How to Use qwen-image-3.0-pro via the hiapi API: curl, Python, and a Working Request

Start generating