Media generation (video, images, music)
NordRouter can generate images, video and music — with the same sk-nr-… key and the same balance as the text models. Pay per use, no tokens or packs.
Server-side only, not from the browser
Calling /media/* straight from a browser will not work — the request fails the CORS check and aborts with Failed to fetch.
This is deliberate. The endpoint needs your key in the Authorization header, and anything that reaches the browser is visible to anyone who opens devtools: the key leaks and strangers spend your balance. No fetch headers can prevent that.
The correct shape:
browser → your server (the key lives here) → nordrouter.comThe finished file is served as an ordinary link — you can drop it straight into <img src> or <video src> on the page, no configuration needed.
How it works
Generation is asynchronous (video and music take from seconds to a couple of minutes):
- Create a task —
POST /media/generatewith a model and parameters. - Wait for the result — poll
GET /media/job/:iduntilstatusisdone. - Fetch the file — from
result_url(a link onnordrouter.com).
1. Create a task
curl https://nordrouter.com/media/generate \
-H "Authorization: Bearer sk-nr-YOUR-KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "image/nano-banana-2",
"input": { "prompt": "a cozy cabin in a winter forest, watercolor", "resolution": "2K" }
}'Response:
{ "id": "beefb531-…", "model": "image/nano-banana-2", "status": "processing",
"poll": "https://nordrouter.com/media/job/beefb531-…" }2. Wait for the result
curl https://nordrouter.com/media/job/beefb531-…{ "id": "beefb531-…", "status": "done",
"result_url": "https://nordrouter.com/media/file/beefb531-…",
"cost_usd": 0.052, "error": null }status: processing → done (or failed). The finished file is at result_url.
🎵 Suno generates two tracks per task: the first is at
result_url, the second atresult_url_2(same link format). You pay per task — both tracks are included.
Webhooks (instead of polling)
Don't want to poll — pass a callback_url to POST /media/generate and we'll push the result with a POST when the task finishes:
curl https://nordrouter.com/media/generate \
-H "Authorization: Bearer sk-nr-YOUR-KEY" -H "Content-Type: application/json" \
-d '{
"model": "image/nano-banana-2",
"input": { "prompt": "…" },
"callback_url": "https://your-server.com/webhooks/nordrouter",
"callback_secret": "your-secret"
}'When the task reaches done/failed, a POST hits your callback_url with the same body as GET /media/job/:id plus an event field:
{ "event": "media.completed", "id": "beefb531-…", "status": "done",
"result_url": "https://nordrouter.com/media/file/beefb531-…", "cost_usd": 0.052, "error": null }event: media.completed or media.failed.
Verify the signature. If you set callback_secret, the X-NR-Signature header carries sha256=<hmac> — HMAC-SHA256 of the raw request body keyed by your secret. Verify it to confirm the request is from us:
import hmac, hashlib
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
assert hmac.compare_digest(expected, request.headers["X-NR-Signature"])Requirements & reliability:
callback_urlmust behttps://and a public address (internal/private IPs are rejected with 400).- Up to 4 delivery attempts with increasing backoff; respond
2xxor we'll retry. - Webhooks don't replace polling — combine them, or use
GET /media/job/:idas a fallback.
More detail
The job lifecycle, signature verification with examples, and delivery reliability — on the Jobs and webhooks page.
3. Models and pricing
GET /media/models returns available models, their parameters and a price estimate:
{ "models": [
{ "id": "image/nano-banana-2", "type": "image",
"fields": [ {"name":"prompt","type":"textarea"}, {"name":"resolution","type":"select","options":["1K","2K","4K"]} ],
"est_usd": 0.052 }
], "retention_days": 14 }Full reference
Every model with its input parameters and price — on the Media: all models & billing page.
Models
95 models are available — the always-current list with parameters and prices lives in GET /media/models. Main families:
| Type | Family | Modes | ≈ price |
|---|---|---|---|
| 🖼 Images | Nano Banana / 2 / Pro (image/nano-banana*) | text→image, edit, up to 4K | from $0.025 |
| 🖼 | Imagen 4 Fast / Ultra (image/imagen4*) | text→image | from $0.025 |
| 🖼 | GPT Image 1.5 / 2 (image/gpt-image-*) | text→image, edit, up to 4K | from $0.025 |
| 🖼 | Flux 2 Pro / Flex (image/flux2-*) | text→image, edit | from $0.03 |
| 🖼 | Seedream v4 / 4.5 / 5 Lite (image/seedream-*) | text→image, edit | from $0.04 |
| 🖼 | Ideogram v3 / Character (image/ideogram-*) | text, remix, character | from $0.02 |
| 🖼 | Qwen / Wan Image / Grok / Z-Image | text→image, edit | from $0.01 |
| 🛠 Utils | Topaz / Recraft (image/*upscale*, image/recraft-remove-bg) | upscale up to 8K, background removal | from $0.01 |
| 🎬 Video | Veo 3.1 Lite / Fast / Quality (video/veo-3.1-*) | text/image→video | from $0.19 |
| 🎬 | Kling 2.1 → 3.0 (video/kling-*) | text/image→video, motion, avatars, up to 4K & 15s | from $0.16 |
| 🎬 | Wan 2.2 → 2.7 (video/wan-*) | text/image/video→video, speech-to-video | from $0.25 |
| 🎬 | Seedance 1.5 / 2 / Fast / Mini (video/seedance-*) | text/image→video, up to 4K, audio | from $0.06 |
| 🎬 | Hailuo 02 / 2.3 (video/hailuo-*) | text/image→video | from $0.08 |
| 🎬 | HappyHorse 1.0 / 1.1 (video/happyhorse-*) | text/image/reference→video, video edit | from $0.70 |
| 🎬 | Grok Video / 1.5 (video/grok-*) | text/image→video, up to 30s | from $0.06 |
| 🎬 | Gemini Omni (video/gemini-omni) | video with audio, up to 4K | from $0.39 |
| 🗣 Avatars | Kling Avatar, OmniHuman, InfiniteTalk, Lip Sync | photo+audio→talking video | from $0.19 |
| 🎵 Music | Suno V4–V5.5 (music/suno-v5) | text/style→2 tracks | ~$0.08 |
| 🎙 Speech | ElevenLabs Turbo / Multilingual (audio/elevenlabs-*) | text→speech, vocal isolation | from $0.04 / 1000 chars |
Parameters
Parameters depend on the model — the exact list is in /media/models (the fields array: name, type, options, default). Common ones:
prompt— description (for all generative models).resolution—1K/2K/4K(images) or480p/720p/1080p/4k(video). Price depends on resolution.aspect_ratio—1:1,16:9,9:16, etc.duration— video length in seconds (most models5/10, Kling 3.0 & Wan 2.7 up to15, Grok up to30). Video is billed per second.sound/generate_audio— audio track (Kling, Seedance). Audio costs more —/media/estimateaccounts for it.mode/quality/rendering_speed— quality tier (Kling 3.0std/pro/4K, GPT Imagemedium/high, IdeogramTURBO/BALANCED/QUALITY).image— source image (edit, image→video; pass a direct link or upload via/media/upload).video_url/audio_url— source video/audio (video-edit, lip sync, avatars).- For music:
title,style,instrumental,version(V4…V5_5).
POST /media/estimate with the same model + input returns the exact price of a specific configuration before you commit.
🧊 3D models
Generate 3D from text or from a photo. The output is GLB (plus OBJ, FBX, STL, USDZ, USD, 3MF depending on the model) together with a preview image — a job returns several files, each with its own extension.
The call is the same as the rest of media: POST /media/generate, then poll GET /media/job/{id}.
curl -X POST https://nordrouter.com/media/generate -H "Authorization: Bearer $NR_KEY" -H "Content-Type: application/json" -d '{"model":"3d/tripo-text","input":{"prompt":"a ceramic mug, matte glaze"}}'| id | input | ≈ price | notes |
|---|---|---|---|
3d/tripo-text, 3d/tripo-image | text, photo | from $0.31 | cheapest, quad mesh, PBR |
3d/hunyuan-rapid-text, -image | text, photo | from $0.43 | fast, 5 file formats |
3d/seed3d-image | photo | from $0.50 | GLB, OBJ, USD, USDZ |
3d/hunyuan-pro-text, -image | text, photo | from $0.71 | up to 1.5M polygons |
3d/meshy-text, -image, -multi-image | text, photo, up to 4 views | from $0.94 | textures up to 8K, character pose |
A photo as the texture. The 3d/meshy-* models accept a separate image (texture_image) or a material description (texture_prompt), so the texture is set independently of the geometry. 3d/meshy-multi-image takes up to four views of the same object for a more accurate mesh.
The price is known only after generation
3D is billed by the compute actually spent, so the exact amount is known after the model has been built. We hold the maximum possible cost, then charge the real one and refund the difference. The final amount is always in cost_usd.
In the studio these models carry a notice, and generation starts only after you accept it.
It takes a while. 3D runs from half a minute to several minutes — longer than an image or a short clip. You do not need to keep the job open: the status is kept, and webhooks are available.
Cost and storage
- Charged to your USD balance by the actual generation cost — the exact amount is in
cost_usdafter completion. - Failed generations (provider errors) are not charged — the reserve is refunded automatically.
- Files are kept on our servers for a limited time (see
retention_daysin/media/models) — download to keep them.