Python quickstart

Video-to-Music Python quickstart

The fastest official path for a coding agent is to install the Python SDK, useSONILO_API_KEY, generate a real soundtrack from a public video URL — no local video file or ffmpeg needed — saveoutput.m4a, and print its file size.

Billing and credits prerequisite

Real generation requires available balance or credits. If the account has no usable balance, Sonilo returns 402 Payment Required or an insufficient balance error and will not generate placeholder audio. Agents should report that balance is required instead of creating fake files.

1. Install and set the API key

python -m venv .venv
source .venv/bin/activate
pip install sonilo
export SONILO_API_KEY="sk_your_api_key"

2. Generate and verify output.m4a

from pathlib import Path

from sonilo import Sonilo

# A small public sample clip — swap for your own video_url, or pass
# video="/path/to/video.mp4" instead. Either way, no local video file
# is required just to try this.
VIDEO_URL = "https://www.w3schools.com/html/mov_bbb.mp4"
OUTPUT = Path("output.m4a")


def is_insufficient_balance_error(exc: Exception) -> bool:
    text = str(exc).lower()
    response = getattr(exc, "response", None)
    status = getattr(exc, "status_code", None) or getattr(response, "status_code", None)
    code = str(getattr(exc, "code", "")).lower()
    return (
        status == 402
        or code == "insufficient_balance"
        or "insufficient balance" in text
        or "payment required" in text
    )


def main() -> None:
    client = Sonilo()  # reads SONILO_API_KEY

    try:
        # No prompt: omitting it gives the best results — the model scores
        # directly off the video's own content, pacing, and cuts.
        track = client.video_to_music.generate(video_url=VIDEO_URL)
    except Exception as exc:
        if is_insufficient_balance_error(exc):
            print("Blocked: insufficient balance or credits. Add balance, then rerun.")
            raise SystemExit(2) from exc
        raise

    track.save(OUTPUT)
    size = OUTPUT.stat().st_size
    if size <= 0:
        raise RuntimeError("output.m4a was empty; do not treat this as a success.")

    print(f"saved {OUTPUT} ({size:,} bytes)")


if __name__ == "__main__":
    main()

This calls the default streaming mode and blocks until the track is complete — the SDK handles the underlying NDJSON stream. For a video with its own dialogue or narration you want to keep, pass preserve_speech=True and use generate_async() instead; see the full API reference for that and every other parameter.