Skip to main content

Video generations

Generate video from a text prompt, optionally guided by reference images, video or audio.

Video generation is asynchronous. You submit a job, receive an id immediately, then poll until the video is ready. It is billed on the real usage reported for the finished job, not a fixed per-video price — see Billing.

POST https://api.ecohash.com/v1/video/generations

Generation modes

A request is in exactly one of four modes, chosen by what you put in reference_images. The modes cannot be combined — a request mixing them is rejected with 400.

ModeYou supplyControl you get
Text to videoprompt onlyThe model composes everything
First frameprompt + 1 image, role: "first_frame"The video opens on your image
First and last framesprompt + 2 images, first_frame and last_frameBoth ends pinned; the model fills the middle
Multimodal referenceprompt + 1–9 images, role: "reference_image" (± video, ± audio)Images guide style and subject; no frame is pinned

The distinction between the last two matters. Reference images influence the whole clip but do not pin any frame. If you need the video to begin or end on a specific image, use the frame roles — describing it in the prompt is not equivalent.

Quick start

curl https://api.ecohash.com/v1/video/generations \
-H "Authorization: Bearer eco_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "ecolink-video-gen-2.0",
"prompt": "a red balloon drifting over a mountain range at dawn",
"resolution": "720p",
"duration": 5,
"aspect_ratio": "16:9"
}'

Parameters

Required

FieldTypeNotes
modelstringecolink-video-gen-2.0
promptstringWhat to generate. Required in every mode, including when you supply reference media

Prompt language — English, Japanese, Indonesian, Spanish and Portuguese. Keep it under about 1000 words. Longer prompts scatter the model's attention: it starts tracking the main subjects and dropping the details, so elements you asked for go missing.

Output controls

FieldTypeDefaultValid values
resolutionstring720p480p, 720p, 1080p, 4k
durationinteger5415 (seconds)
aspect_ratiostringadaptive16:9, 4:3, 1:1, 3:4, 9:16, 21:9, adaptive
generate_audiobooleantruetrue, false

resolution sets the output size and the pricing band, so it is the single parameter with the largest effect on cost. 480p and 720p share a band; 1080p and 4k each have their own. 4k output is 10-bit H.265 — richer colour, but some players will not open it.

duration is clamped, not rejected: below 4 becomes 4, above 15 becomes 15. You get a video rather than an error, so if the length surprises you, check what you sent. Duration is a direct input to the bill.

aspect_ratio defaults to adaptive, which picks a ratio to suit the request — from the prompt for text-only jobs, from the first reference image or video when you supply one. The ratio actually used is returned on the finished job. If your chosen ratio does not match a reference image, the image is cropped from the centre.

generate_audio is on by default — the model composes speech, effects and background to match the prompt and picture. Set false for silent video. For spoken lines, put the words in double quotes in your prompt, e.g. the captain turns and says "we sail at dawn". Generated audio is mono regardless of any reference audio you supply.

Reference media

FieldTypeNotes
reference_imagesobject[]Up to 9. Each entry is { "url": "...", "role": "..." }
reference_image_urlsstring[]Shorthand: a plain URL list, all treated as reference_image
reference_video_urlsstring[]Up to 3 reference videos
reference_audio_urlsstring[]Up to 3 reference audio tracks
reference_video_urlstringA single reference video — shorthand for a one-element reference_video_urls
reference_audio_urlstringA single reference audio track — shorthand for a one-element reference_audio_urls

The singular and plural forms are mutually exclusive for a given kind — send reference_video_url or reference_video_urls, not both. Likewise reference_images and reference_image_urls. Sending both returns 400.

When you supply several videos or several audio clips, the combined duration of each kind must stay within 15 seconds — see Input media requirements.

Image roles

roleMeaningCount
first_frameThe video opens on this image1
last_frameThe video ends on this image. Requires a first_frame1
reference_imageGuides style and subject; pins nothing1–9
(omitted)Treated as reference_image

Rules, all enforced before the job is created:

  • reference_image cannot be mixed with first_frame or last_frame.
  • last_frame without a first_frame is rejected.
  • At most one first_frame and one last_frame.
  • first_frame/last_frame cannot be combined with reference video or audio.
  • Reference audio needs at least one image or a reference video — audio cannot be the only reference.

The first and last frame may be the same image. If the two have different aspect ratios, the first frame wins and the last is cropped to match.

note

Omitting role means reference_image, never first_frame. If you want the opening frame pinned, say so explicitly.

Accepted but not used by this model

These belong to the shared video request shape and are accepted without error, but ecolink-video-gen-2.0 does not act on them. Listed so you can tell a silently-ignored field from one that is not working.

FieldWhat happens
seedIgnored. This model has no seeded output; identical requests will differ
num_framesOnly derives duration (num_frames ÷ fps) when duration is omitted. Never sets frame count
fpsOnly derives duration, as above. Does not set the output frame rate
width, heightIgnored. Use resolution and aspect_ratio
num_inference_stepsIgnored

Watermarking is off for all generated video and cannot be enabled.

Input media requirements

All reference media is supplied as a publicly downloadable URL — the generator fetches it directly. Base64 payloads are not accepted.

These are checked when the job is submitted, so a file that breaks a rule comes back immediately with a code of reference_media_unreachable or reference_media_too_long.

Images

FormatsJPEG, PNG, WebP, BMP, TIFF, GIF, HEIC, HEIF
Dimensions300–6000 px on each side
Aspect ratiobetween 0.4 and 2.5 (width ÷ height)
Size30 MB per image

Video

ContainerMP4 (.mp4), QuickTime (.mov)
Video codecH.264/AVC, H.265/HEVC
Audio codecAAC, MP3
Resolution480p, 720p, 1080p, 4k
Duration2–15 seconds each; with several videos, 15 seconds combined
Countup to 3
Dimensions300–6000 px per side; total pixels between 409,600 and 8,295,044
Aspect ratiobetween 0.4 and 2.5
Frame rate24–60 fps
Size200 MB each

Audio

FormatsWAV, MP3
Duration2–15 seconds each; with several clips, 15 seconds combined
Countup to 3
Size15 MB each

Output dimensions

resolution and aspect_ratio together determine the exact pixel size:

Resolution16:94:31:13:49:1621:9
480p864×496752×560640×640560×752496×864992×432
720p1280×7201112×834960×960834×1112720×12801470×630
1080p1920×10801664×12481440×14401248×16641080×19202206×946
4k3840×21603326×24942880×28802494×33262160×38404398×1886

With aspect_ratio: "adaptive" the model picks a row; the finished job reports which.

Examples

First frame

The video opens on your image and animates forward.

curl https://api.ecohash.com/v1/video/generations \
-H "Authorization: Bearer eco_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "ecolink-video-gen-2.0",
"prompt": "the boat pulls away from the dock as gulls scatter",
"resolution": "1080p",
"duration": 6,
"reference_images": [
{ "url": "https://example.com/harbour.jpg", "role": "first_frame" }
]
}'

First and last frames

Both ends are pinned; the model generates the transition between them.

curl https://api.ecohash.com/v1/video/generations \
-H "Authorization: Bearer eco_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "ecolink-video-gen-2.0",
"prompt": "slow push in from the wide harbour view to the lighthouse at dusk",
"resolution": "1080p",
"aspect_ratio": "16:9",
"duration": 8,
"reference_images": [
{ "url": "https://example.com/harbour-wide.jpg", "role": "first_frame" },
{ "url": "https://example.com/lighthouse.jpg", "role": "last_frame" }
]
}'

Multimodal reference

Several images guide subject and style; no frame is pinned. A reference video can supply motion, and reference audio can supply timing.

curl https://api.ecohash.com/v1/video/generations \
-H "Authorization: Bearer eco_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "ecolink-video-gen-2.0",
"prompt": "a documentary shot of the same harbour in winter, muted colour grade",
"resolution": "720p",
"duration": 10,
"reference_images": [
{ "url": "https://example.com/style-1.jpg", "role": "reference_image" },
{ "url": "https://example.com/style-2.jpg", "role": "reference_image" }
],
"reference_video_urls": ["https://example.com/camera-move.mp4"],
"reference_audio_urls": ["https://example.com/ambient-timing.mp3"]
}'

Each of reference_video_urls and reference_audio_urls takes up to 3. The singular reference_video_url / reference_audio_url still work as a one-item shorthand, but prefer the list form.

The shorthand form is equivalent when every image is reference material:

"reference_image_urls": ["https://example.com/style-1.jpg", "https://example.com/style-2.jpg"]

Silent, vertical, cheapest band

curl https://api.ecohash.com/v1/video/generations \
-H "Authorization: Bearer eco_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "ecolink-video-gen-2.0",
"prompt": "rain running down a window, neon reflections",
"resolution": "480p",
"aspect_ratio": "9:16",
"duration": 4,
"generate_audio": false
}'

Response

202 Accepted — the job is queued. No video yet.

{
"id": "b3f1c2a4-...",
"status": "processing",
"model": "ecolink-video-gen-2.0"
}

Poll for completion

GET https://api.ecohash.com/v1/video/jobs/{id}

status moves queuedprocessingcompleted, or ends at failed. Generation takes minutes; poll every few seconds rather than continuously.

{
"id": "b3f1c2a4-...",
"status": "completed",
"model": "ecolink-video-gen-2.0",
"width": 1280,
"height": 720,
"created_at": "2026-07-14T09:00:00Z",
"completed_at": "2026-07-14T09:01:12Z",
"url": "https://.../b3f1c2a4.mp4",
"billing": {
"charge_usd": 0.42,
"pricing_tier": "480_720_no_video",
"resolution": "720p",
"duration_seconds": 5,
"billable_tokens": 100000,
"usage_source": "upstream_reported"
}
}

url is a temporary download link valid for 30 minutes. Fetch and store the file if you need it longer; polling again issues a fresh link.

On failure, status is "failed" and the job carries three fields describing what went wrong:

{
"id": "b3f1c2a4-...",
"status": "failed",
"model": "ecolink-video-gen-2.0",
"created_at": "2026-07-14T09:00:00Z",
"completed_at": "2026-07-14T09:00:48Z",
"error": "The generated audio was flagged by the content-safety filter — please try a different prompt, or turn off audio generation.",
"code": "content_filtered.output_audio",
"retryable": false
}
FieldUse it for
codeBranching in your client — a stable value, listed under Errors
retryableWhether resubmitting the same request could succeed
errorShowing a person what happened. Wording changes; do not match on it

Failed jobs are not billed.

Estimate cost first

POST https://api.ecohash.com/v1/video/quote

Takes the same body as a submit and returns the pricing band and an estimated range without generating anything.

{
"pricing_tier": "480_720_no_video",
"unit_price_per_1m_tokens": 7.14,
"estimated_tokens_min": 85000,
"estimated_tokens_max": 115000,
"estimated_cost_min_usd": 0.607,
"estimated_cost_max_usd": 0.821,
"currency": "USD",
"note": "Final charge is based on actual tokens reported by the provider."
}

Billing

A finished job is charged on the tokens reported for it, at a rate set by two things:

Resolution band480p/720p · 1080p · 4k
Reference videowhether reference_video_url was supplied

Six rates in total. Supplying a reference video uses a lower per-token rate than generating from a prompt alone.

Because the charge follows real usage, a quote is a range rather than a fixed price. duration and resolution move it most.

For current rates call GET /platform/models/ecolink-video-gen-2.0/pricing, which returns the live per-band figures — read them rather than hard-coding.

Failed jobs are never charged.

Limits

  • 10 submissions per 60 seconds per account for this model. Over it returns 429.
  • Failed submissions do not count, so a rejected request can be retried immediately.
  • Up to 3 reference videos and 3 reference audio tracks per request, each kind capped at 15 seconds combined.
  • Reference media must be a URL; base64 payloads are not accepted.

Errors

A failure — whether it comes back from the submit call or shows up later on a polled job — carries a code. Branch on that. The error text is written for people and is reworded whenever we can explain something better, so matching on it will break.

{
"error": "A reference image was rejected by the content-safety filter — please replace it and try again. It may contain an identifiable real person.",
"code": "content_filtered.input_image"
}

Error codes

codeHTTPRetry?What to change
invalid_request400noA parameter is missing or out of range. Check resolution, aspect ratio and duration against Parameters.
reference_media_unreachable400noA reference URL could not be fetched, or the file was not readable media. It must be publicly downloadable.
reference_media_too_long400noA reference clip's duration is out of range — each audio or video must be 2–15 seconds, and several of one kind must total ≤15 seconds.
content_filtered.input_text400noYour prompt was rejected. Rephrase it.
content_filtered.input_image400noA reference image was rejected. Replace it.
content_filtered.input_video400noA reference video was rejected. Replace it.
content_filtered.input_audio400noA reference audio clip was rejected. Replace it.
content_filtered.output_text400noGenerated text was flagged. Try a different prompt.
content_filtered.output_image400noA generated image was flagged. Try a different prompt.
content_filtered.output_video400noThe generated video was flagged. Try a different prompt.
content_filtered.output_audio400noThe generated audio was flagged. Try a different prompt, or set generate_audio to false.
capacity_exceeded429yesGeneration capacity is saturated. Wait and retry.
generation_timeout504yesGeneration ran past its deadline. Retry, or reduce duration / resolution.
internal_error502yesA temporary fault. Retry.
model_unavailable502 / 503noThe model cannot serve requests right now. This one is ours, not yours — retrying the same request will not help. If it persists, contact support.

The HTTP column applies when the error comes back from the submit call. A job that fails during generation returns 200 on the poll with status: "failed" — there the code is the only signal, which is why it is worth branching on.

Two shortcuts worth building in:

  • content_filtered. prefix — match the prefix to handle every content-safety rejection in one branch, and the exact code when you want to tell a user which file to swap. New modalities added later keep working with a prefix match.
  • retryable — polled jobs also return a boolean retryable, so you can drive backoff from it without keeping a list of codes in your client.

Where errors surface

Your job is submitted for generation as part of your request, so problems with the request itself — a missing parameter, an unreachable reference URL, an over-long clip — come back immediately rather than as a failure discovered minutes later.

Content-safety rejections can happen at either point. A reference file is checked on submit; generated output can only be checked once it exists, so content_filtered.output_* always arrives on a polled job.

A job accepted with 202 can still end at status: "failed". Check the polled job rather than assuming acceptance means success. Failed jobs are not billed.

Authentication and account errors are not video-specific: 401 for a missing or invalid API key, 402 for insufficient credits, 404 for an unknown model. See Errors for those.