Media generation (video, images, music)
NordRouter can generate images, video, and music using the same sk-nr-… key and balance as text models. Pay per generation, with no tokens or packages.
Send requests from your backend only, not from the browser
You cannot call /media/* directly from the browser — the request will fail the CORS check and return Failed to fetch.
This is intentional. The endpoint requires your key in the Authorization header, and everything sent to the browser is visible to anyone who opens developer tools: your key will be exposed, allowing others to spend your balance. No headers in fetch can prevent this.
The correct flow:
browser → your server (the key stays here) → nordrouter.comThe generated file is served via a regular link — you can use it directly in <img src> or <video src> on your page without any additional configuration.
How it works
Generation is asynchronous (video and music take anywhere from a few seconds to a couple of minutes):
- Create a job —
POST /media/generatewith the model and parameters. - Wait for the result — poll
GET /media/job/:iduntilstatusbecomesdone. - Retrieve the file — via
result_url(a link onnordrouter.com).
1. Create a job
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-…" }Errors always use the same format: { "error": "понятный текст", "code": "missing_param" }. Use code to branch in your code (invalid_input, missing_param, unknown_model, insufficient_balance, rate_limited, no_auth…); error is a human-readable message.
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 generated file is available at result_url.
🎵 Suno generates two tracks per job: the first is in
result_url, the second inresult_url_2(the same link format). The price is per job and includes both tracks.
Webhooks (instead of polling)
To avoid polling, include the callback_url field in POST /media/generate, and we will send you the result via a POST request when the job 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": "ваш-секрет"
}'When the job reaches done/failed, a POST request is sent to callback_url with the same body returned by 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.
Signature verification. If you set callback_secret, the X-NR-Signature header will contain sha256=<hmac> — an HMAC-SHA256 of the request body using your secret. Verify it to confirm that the request came 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 and reliability:
callback_urlmust usehttps://and a public address (internal/private IPs are rejected with a 400 error).- Up to 4 delivery attempts with increasing delays; return
2xx, otherwise we will retry. - Webhooks do not disable polling — you can combine both or use
GET /media/job/:idas a fallback.
Learn more
For the job lifecycle, signature verification examples, and delivery reliability, see Jobs and webhooks.
3. List models and prices
GET /media/models returns the available models, their parameters, and estimated costs:
{ "models": [
{ "id": "image/nano-banana-2", "type": "image",
"fields": [ {"name":"prompt","type":"textarea"}, {"name":"resolution","type":"select","options":["1k","2k","4k"]},
{"name":"safety_filter","type":"bool","default":true} ],
"est_usd": 0.052 }
], "retention_days": 14 }Full reference
For all models with their input parameters and prices, see Media: all models and billing.
Models
95 models are available — the full, up-to-date list with parameters and pricing is always available at GET /media/models. Main families:
| Type | Family | Modes | ≈ price |
|---|---|---|---|
| 🖼 Images | Nano Banana / 2 / Pro (image/nano-banana*) | text→image, editing, 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, editing, up to 4K | from $0.025 |
| 🖼 | Flux 2 Pro / Flex (image/flux2-*) | text→image, editing | from $0.03 |
| 🖼 | Seedream v4 / 4.5 / 5 Lite (image/seedream-*) | text→image, editing | from $0.04 |
| 🖼 | Ideogram v3 / Character (image/ideogram-*) | text, remix, character | from $0.02 |
| 🖼 | Qwen / Wan Image / Grok / Z-Image | text→image, editing | from $0.01 |
| 🛠 Utilities | Topaz / Recraft (image/*upscale*, image/recraft-remove-bg) | upscaling up to 8K, background removal | from $0.01 |
| 🎬 Video | Veo 3.1 Lite / Fast / Quality (video/veo-3.1-*) | text/photo→video | from $0.19 |
| 🎬 | Kling 2.1 → 3.0 (video/kling-*) | text/photo→video, motion, avatars, up to 4K and 15s | from $0.16 |
| 🎬 | Wan 2.2 → 2.7 (video/wan-*) | text/photo/video→video, speech-to-video | from $0.25 |
| 🎬 | Seedance 1.5 / 2 / Fast / Mini (video/seedance-*) | text/photo→video, up to 4K, audio | from $0.06 |
| 🎬 | Hailuo 02 / 2.3 (video/hailuo-*) | text/photo→video | from $0.08 |
| 🎬 | HappyHorse 1.0 / 1.1 (video/happyhorse-*) | text/photo/reference→video, video editing | from $0.70 |
| 🎬 | Grok Video / 1.5 (video/grok-*) | text/photo→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 V6 / V6 Wild / V6 Mini (music/suno-*) | text/style→2 tracks, cover, continuation, vocals, stems, sounds | ~$0.08 |
| 🎙 Speech | ElevenLabs Turbo / Multilingual (audio/elevenlabs-*) | text→speech, voice isolation | from $0.04 / 1000 chars |
Parameters
Parameters depend on the model — the exact list is in /media/models (the fields field: name, type, options, default). Names use a consistent style (snake_case), and values are lowercase. Common parameters:
prompt— description (for all generative models).resolution—1k/2k/4k(images) or480p/720p/768p/1080p/4k(video). Pricing depends on resolution.aspect_ratio—1:1,16:9,9:16, etc.; models with size selection also support1:1-hd.duration— video duration in seconds (5/10for most models, up to15for Kling 3.0 and Wan 2.7, up to30for Grok). Video is billed per second.audio— audio track in the video (true/false). Audio costs extra — the estimate from/media/estimateaccounts for this.mode/quality/render_mode— quality level (Kling 3.0standard/pro/4k, GPT Imagemedium/high, Ideogramfast/balanced/quality).safety_filter— adult content filter (enabled by default).enhance_prompt— the model expands the prompt with more detail (Qwen, Flux 3).file_format— file format (png/jpegfor images,glb/obj/fbx/stl/usdzfor 3D).image— input image (editing, photo→video; pass a direct link or upload via/media/upload).video_url/audio_url— input video/audio (video-edit, lip sync, avatars).
Music: title, style, instrumental, version (v6, v6-wild, v6-mini), advanced (fine-tuning: custom title, style, and duration), exclude_styles, vocal (female/male), continue_from (the second to continue from); for sounds — loop, bpm, tonality; stem separation — mode (vocals, stems, track) and track (which track to extract). Fine-grained style controls: style_weight, weirdness, audio_weight (0…1).
Speech: text, voice (a short voice name from options, such as james, bella, Zephyr), speed; for Gemini models — style, pace, accent, voice_description, scene, temperature. A dialogue with two voices:
{ "model": "audio/gemini-3.1-flash-tts",
"input": {
"voices": [ { "voice": "Kore", "style": "newscaster" }, { "voice": "Puck", "pace": "fast" } ],
"dialogue": [ { "speaker": 1, "text": "Hello! Our guest today is…" }, { "speaker": 2, "text": "Thanks for having me." } ]
} }To get the exact price for a specific configuration before running it, call POST /media/estimate with the same model + input.
Previous parameter names still work
On October 19, 2026, parameter names and values were standardized (snake_case, lowercase values). If your integration passes names and values from the previous version of the documentation, no changes are needed: the server accepts them too, with the same results and pricing. Only the new names and values appear in /media/models and this documentation.
🧊 3D Models
Generate 3D models from text or a photo. The output is GLB (plus OBJ, FBX, STL, USDZ, USD, or 3MF, depending on the model) and a preview image: each job returns multiple files, each with its own extension.
Use the same calls as for other media: POST /media/generate and 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":"ceramic mug, matte glaze"}}'| id | input | ≈ price | features |
|---|---|---|---|
3d/tripo-text, 3d/tripo-image | text, photo | from $0.31 | lowest cost, 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.5 million polygons |
3d/meshy-text, -image, -multi-image | text, photo, up to 4 views | from $0.94 | textures up to 8K, character pose |
Photo as a texture. The 3d/meshy-* models accept a separate image (texture_photo) or a material description (texture_text) — the texture is specified independently of the geometry. 3d/meshy-multi-image takes up to four views of the same object for more accurate geometry.
The price is known only after generation
3D generation is billed based on the resources actually used, so the exact amount is known only after the model has been generated. We reserve the maximum possible cost, then charge the actual amount and refund the difference once the calculation is complete. The final amount is always available in the cost_usd field.
In the studio, these models are marked with a badge, and generation starts only after you agree.
This takes time. 3D generation takes anywhere from half a minute to several minutes — longer than an image or a short video. You do not need to keep the job open: its status is saved, and webhooks are available.
Pricing and Storage
- Your USD balance is charged the actual generation cost — the exact amount is available in
cost_usdafter completion. - Failed generations are not billed — the reserved amount is refunded automatically.
- Files are stored on our servers for a limited time (see
retention_daysin/media/models) — download them to keep them.