Docs menu

Video to Music

For AI agents1 call free, no credit card required.
Generate music from a video. By default (mode=stream) returns a streaming NDJSON response with one or more parallel audio streams. Pass mode=async to instead get 202 Accepted with a task_id; poll GET /v1/tasks/:task_id until the task reaches a terminal status.

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.

POST/v1/video-to-musicmultipart/form-data
API URLhttps://api.sonilo.com/v1/video-to-music

Send 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_key

Store 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

videofile
Video file upload. Provide either video or video_url, not both. Max file size: default 300MB. Max duration: 6 minutes.
video_urlstring
URL to a video file. Provide either video 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.
modestring
Optional. stream (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_formatstring
Optional. m4a (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).
promptstring
Optional text prompt to guide music generation, up to 2000 characters. For best results, omit this and let the model generate based on video content.
segmentsstring
Optional JSON array of timed segment prompts; the music changes style at each boundary. Each item has start (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_speechboolean
Optional boolean, default false. 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.
duckingboolean
Optional boolean, default false. 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_numinteger
Optional. How many distinct music variants to generate in one request, 1-10 (default 1). 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_influencenumber
Optional number, 0 to 1 (default 0.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.
stemsboolean
Optional boolean, default false. 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

titleevent
Generated track title. Appears once near the start of the stream.
audio_chunkevent
Base64-encoded AAC/fMP4 audio fragment. Includes stream_index (0-based) and num_streams (total parallel outputs). Group chunks by stream_index and append in order.
errorevent
Generation failed. Contains code and message.
completeevent
Stream finished successfully.

Next steps