Skip to content

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.com

The 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):

  1. Create a task — POST /media/generate with a model and parameters.
  2. Wait for the result — poll GET /media/job/:id until status is done.
  3. Fetch the file — from result_url (a link on nordrouter.com).

1. Create a task ​

bash
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:

json
{ "id": "beefb531-…", "model": "image/nano-banana-2", "status": "processing",
  "poll": "https://nordrouter.com/media/job/beefb531-…" }

2. Wait for the result ​

bash
curl https://nordrouter.com/media/job/beefb531-…
json
{ "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 at result_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:

bash
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:

json
{ "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:

python
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_url must be https:// and a public address (internal/private IPs are rejected with 400).
  • Up to 4 delivery attempts with increasing backoff; respond 2xx or we'll retry.
  • Webhooks don't replace polling — combine them, or use GET /media/job/:id as 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:

json
{ "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:

TypeFamilyModes≈ price
🖼 ImagesNano Banana / 2 / Pro (image/nano-banana*)text→image, edit, up to 4Kfrom $0.025
🖼Imagen 4 Fast / Ultra (image/imagen4*)text→imagefrom $0.025
🖼GPT Image 1.5 / 2 (image/gpt-image-*)text→image, edit, up to 4Kfrom $0.025
🖼Flux 2 Pro / Flex (image/flux2-*)text→image, editfrom $0.03
🖼Seedream v4 / 4.5 / 5 Lite (image/seedream-*)text→image, editfrom $0.04
🖼Ideogram v3 / Character (image/ideogram-*)text, remix, characterfrom $0.02
🖼Qwen / Wan Image / Grok / Z-Imagetext→image, editfrom $0.01
🛠 UtilsTopaz / Recraft (image/*upscale*, image/recraft-remove-bg)upscale up to 8K, background removalfrom $0.01
🎬 VideoVeo 3.1 Lite / Fast / Quality (video/veo-3.1-*)text/image→videofrom $0.19
🎬Kling 2.1 → 3.0 (video/kling-*)text/image→video, motion, avatars, up to 4K & 15sfrom $0.16
🎬Wan 2.2 → 2.7 (video/wan-*)text/image/video→video, speech-to-videofrom $0.25
🎬Seedance 1.5 / 2 / Fast / Mini (video/seedance-*)text/image→video, up to 4K, audiofrom $0.06
🎬Hailuo 02 / 2.3 (video/hailuo-*)text/image→videofrom $0.08
🎬HappyHorse 1.0 / 1.1 (video/happyhorse-*)text/image/reference→video, video editfrom $0.70
🎬Grok Video / 1.5 (video/grok-*)text/image→video, up to 30sfrom $0.06
🎬Gemini Omni (video/gemini-omni)video with audio, up to 4Kfrom $0.39
🗣 AvatarsKling Avatar, OmniHuman, InfiniteTalk, Lip Syncphoto+audio→talking videofrom $0.19
🎵 MusicSuno V4–V5.5 (music/suno-v5)text/style→2 tracks~$0.08
🎙 SpeechElevenLabs Turbo / Multilingual (audio/elevenlabs-*)text→speech, vocal isolationfrom $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) or 480p/720p/1080p/4k (video). Price depends on resolution.
  • aspect_ratio — 1:1, 16:9, 9:16, etc.
  • duration — video length in seconds (most models 5/10, Kling 3.0 & Wan 2.7 up to 15, Grok up to 30). Video is billed per second.
  • sound / generate_audio — audio track (Kling, Seedance). Audio costs more — /media/estimate accounts for it.
  • mode / quality / rendering_speed — quality tier (Kling 3.0 std/pro/4K, GPT Image medium/high, Ideogram TURBO/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}.

bash
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"}}'
idinput≈ pricenotes
3d/tripo-text, 3d/tripo-imagetext, photofrom $0.31cheapest, quad mesh, PBR
3d/hunyuan-rapid-text, -imagetext, photofrom $0.43fast, 5 file formats
3d/seed3d-imagephotofrom $0.50GLB, OBJ, USD, USDZ
3d/hunyuan-pro-text, -imagetext, photofrom $0.71up to 1.5M polygons
3d/meshy-text, -image, -multi-imagetext, photo, up to 4 viewsfrom $0.94textures 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_usd after 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_days in /media/models) — download to keep them.