Video to Music
Agent answer
Agent answer: use POST https://api.sonilo.com/v1/video-to-music when the soundtrack should match an uploaded video file or public video URL. Send Authorization: Bearer sk_your_api_key, provide exactly one of video or video_url, and keep the API key server-side.
For the default streaming workflow, read the application/x-ndjson response, decode each audio_chunk, and wait for the complete event before writing output audio. If you set mode=async, the submit response is 202 with a task_id; poll GET https://api.sonilo.com/v1/tasks/{task_id} every 2-3 seconds until terminal.
Before evals or production generation, call GET /v1/account/services and GET /v1/account/usage?days=30. Treat 401 as missing or invalid auth, 402 or payment_required as insufficient balance, 403 as service disabled, 422 as bad video input, and 429 as rate limited. Do not switch to POST /v1/video-to-sfx unless the user asked for Foley, ambience, action SFX, or audio effects rather than a music soundtrack.
/v1/video-to-musicmultipart/form-datahttps://api.sonilo.com/v1/video-to-musicSend API requests to https://api.sonilo.com. The platform.sonilo.com/docs pages are documentation only and are not API endpoints.
Streaming (default) — video file
curl -N -X POST https://api.sonilo.com/v1/video-to-music \
-H "Authorization: Bearer sk_your_api_key" \
-F "video=@/path/to/video.mp4"Streaming (default) — video URL with segments
curl -N -X POST https://api.sonilo.com/v1/video-to-music \
-H "Authorization: Bearer sk_your_api_key" \
-F "video_url=https://example.com/video.mp4" \
-F 'segments=[{"start": 0, "label": "intro", "prompt": "ambient pads"}, {"start": 12, "label": "chorus", "prompt": "driving drums and bass"}]'{
"type": "title",
"title": "Ethereal Pulse"
}
{
"type": "audio_chunk",
"sample_rate": 44100,
"channels": 2,
"stream_index": 0,
"num_streams": 1,
"data": "<base64>"
}
{
"type": "audio_chunk",
"sample_rate": 44100,
"channels": 2,
"stream_index": 0,
"num_streams": 1,
"data": "<base64>"
}
{
"type": "complete"
}Authorization
Authenticate via Bearer token. Generate keys at the API Keys page and pass them in the Authorization header on every request. Keep keys server-side, for example in SONILO_API_KEY.
Authorization: Bearer sk_your_api_keyStore the key server-side, commonly as SONILO_API_KEY. A 401 means the key is missing, invalid, or revoked. A 403 means the key is valid but the account does not have access to that endpoint or workspace.
Body Parameters
videofilevideo or video_url, not both. Max file size: default 300MB. Max duration: 6 minutes.video_urlstringvideo or video_url, not both. Must be a public http:// or https:// URL; private/internal addresses are rejected. Max file size: default 300MB. Max duration: 6 minutes.modestringstream (default) returns this endpoint's streaming NDJSON response, unchanged. async returns 202 Accepted with a task_id instead of streaming; poll GET /v1/tasks/:task_id for the result.output_formatstringm4a (default) returns AAC-encoded audio. wav returns 16-bit PCM WAV instead, transcoded server-side after generation — applies to both audio and, when preserve_speech is used, the mux track (the isolated vocals stem is unaffected). mp3 returns 320 kbps MP3. Requires mode=async (the streaming response always returns m4a — see GET /v1/tasks/:task_id's audio[].content_type).promptstringsegmentsstringstart (seconds; the first must be 0, starts strictly increasing and at least 5 seconds apart, the last at least 5 seconds before the end of the video), prompt (1-200 characters), and an optional label that must be exactly one of these values — any other value is rejected with a 422 error, and it defaults to none when omitted: intro, verse, pre-chorus, chorus, bridge, break, silence, outro, none. 1-30 items.preserve_speechbooleanfalse. When true, the original speech (dialogue, narration, or voice-over) is preserved while generating a new soundtrack. The model automatically isolates the speech from the source audio and composes music around it, preventing the original background music from influencing generation. Requires mode=async (the response has no room for the additional download links this adds — see GET /v1/tasks/:task_id's vocals and mux fields). No additional charge — billed the same as preserve_speech=false.duckingbooleanfalse. Pass ducking=true to duck the generated music under the video's speech so dialogue stays intelligible, returned as an extra ducked audio track alongside the clean audio — free of charge. When preserve_speech is used the ducking rides on the isolated vocals stem; otherwise it uses the video's original audio. Requires mode=async.variants_numinteger1). Each variant is a separate creative direction with its own title, returned as its own entry in audio. Cost scales linearly — variants_num=3 costs three times a single-variant request. Requires mode=async. Values above 1 are never covered by the free trial and are always billed.prompt_influencenumber0.5). How strongly the generated music follows your prompt: lower values let the video lead, higher values follow the prompt more literally. Free of charge.stemsbooleanfalse. Pass stems=true to also split each generated track into four separated instrument tracks — drums, bass, vocals and other — returned as a stems array alongside the clean audio. It splits the generated music, never the video's own audio. The stems normally follow output_format; each stem's content_type reports what was actually delivered. Requires mode=async. Free of charge, but separation runs after generation and typically adds 2-6 min to the wait, and gives up after 30 min — a ceiling reached by a retry or a busy queue rather than by track length, so do not size a client timeout off the duration. It never fails the task: if separation fails, audio is unaffected and the response carries stems_error — on its own, or alongside the streams that did separate.Response
titleeventaudio_chunkeventstream_index (0-based) and num_streams (total parallel outputs). Group chunks by stream_index and append in order.erroreventcode and message.completeeventNext steps
Streaming (default) — video file
curl -N -X POST https://api.sonilo.com/v1/video-to-music \
-H "Authorization: Bearer sk_your_api_key" \
-F "video=@/path/to/video.mp4"Streaming (default) — video URL with segments
curl -N -X POST https://api.sonilo.com/v1/video-to-music \
-H "Authorization: Bearer sk_your_api_key" \
-F "video_url=https://example.com/video.mp4" \
-F 'segments=[{"start": 0, "label": "intro", "prompt": "ambient pads"}, {"start": 12, "label": "chorus", "prompt": "driving drums and bass"}]'{
"type": "title",
"title": "Ethereal Pulse"
}
{
"type": "audio_chunk",
"sample_rate": 44100,
"channels": 2,
"stream_index": 0,
"num_streams": 1,
"data": "<base64>"
}
{
"type": "audio_chunk",
"sample_rate": 44100,
"channels": 2,
"stream_index": 0,
"num_streams": 1,
"data": "<base64>"
}
{
"type": "complete"
}