List Sume's music and TTS router engines in Python before deploy

A short Python script reads the music and TTS router model lists so a deploy check can confirm your pinned engine ids and fixed prices still exist.

5 min readSume
All posts

Two GET routes return the live engine lists: GET /v1/music-router/models and GET /v1/tts-router/models. A twenty-line Python script can print each id with its price fields, so a deploy step can fail when an engine id you pinned has disappeared. The response shape is data.models[] for both, as the API reference and the Music Router page describe.

Vendors keep releasing audio models (this week brought new voice and music announcements), and a pinned id is only safe if you notice when it changes. The catalog is the cheap place to notice.

What each catalog row holds

A music row has id, name, routing (auto or pass_through), an optional resolves_to, capabilities (text to music, image conditioning, lyrics, max_prompt_characters), pricing and constraints. Pricing carries the provider list in micros, the billable amount in micros and a formula string. A TTS row has capabilities.max_characters, list_usd_micros_per_character, billable_margin and the same formula field.

Fields worth reading in a deploy check (Sume OpenAPI schema, checked 2026-10-10)
RouterFieldWhy check it
MusicidIs your pinned engine still listed
Musicresolves_toWhere sume/music-auto points today
Musicpricing.billable_usd_micros_per_audioFixed price per track (125000 micros = $0.125)
TTSidIs sonic-3.6 or your pinned id still listed
TTSpricing.billable_marginMargin applied to the per-character list
TTScapabilities.max_charactersPer-job transcript cap

The script

It uses only the standard library and reads your key from SUME_API_KEY. It prints one line per engine. Run it in CI after setting the variable, and compare the output with the ids in your config.

The script prints raw TTS fields instead of computing a dollar rate, because the billable amount also depends on rounding up to a whole cent per job. Use the formula string in the catalog row when you need the exact rule.

import json, os, urllib.request

BASE = "https://api.sume.com"

def get(path):
    req = urllib.request.Request(
        BASE + path,
        headers={"Authorization": "Bearer " + os.environ["SUME_API_KEY"]},
    )
    with urllib.request.urlopen(req, timeout=30) as resp:
        return json.load(resp)["data"]["models"]

for m in get("/v1/music-router/models"):
    p = m["pricing"]
    print("music", m["id"], m.get("resolves_to", "-"),
          p["billable_usd_micros_per_audio"] / 1e6)

for m in get("/v1/tts-router/models"):
    p = m["pricing"]
    print("tts", m["id"], p["list_usd_micros_per_character"],
          p["billable_margin"])

What the check should do

Turn the printout into assertions. Fail the deploy when a pinned music or TTS id is missing, when sume/music-auto now resolves to something you did not test, or when the price field moved. Do not fail on sonic-latest merely moving, since it is an alias that moves by design; log it instead so the change is visible.

  • Assert that your pinned ids appear in data.models[].
  • Log resolves_to for the auto row on every deploy.
  • Alert, rather than fail, on a changed price so a person decides.
  • Keep the output with the deploy record.

An unknown id

If your code sends an id that is not in the list, the music route answers 400 model_not_found with a catalog_url; the catalog check lets you catch that before a job is queued, not after a batch has half run. The same applies to the TTS Router. Remember that TTS 1.0 rejects model entirely, so a pinned id belongs on the router route.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume