Streamlit image generator app with the Sume Images API (30 lines)
A 30-line Streamlit app: pick a Sume image model from the live catalog, type a prompt, show the result and the billed cost. Handles 200 and 202 responses.

A Streamlit image app for Sume needs about 30 lines: fetch the model list from GET /v1/images/models, show it in a selectbox, post the prompt to POST /v1/images, then render the first data[].url and the billed usage.cost. Save it as app.py, export SUME_API_KEY and run streamlit run app.py.
The one detail that trips demos is the response code. Sume waits up to 30 seconds for a synchronous image, then returns 202 with a job envelope instead of the image body, so the app should branch on status rather than assume data exists.
What does the app need from the API?
| Step | Call | Field used |
|---|---|---|
| Fill the model picker | GET /v1/images/models | data[].id, data[].name |
| Generate | POST /v1/images | model, prompt, n |
| Show the image | 200 response | data[0].url |
| Show the price | 200 response | usage.cost (billed USD) |
| Slow job | 202 response | data.status_url |
What is the code?
Keep the key in the environment, never in the page. Streamlit reruns the script on every interaction, so the generate call sits behind a button.
import os, requests, streamlit as st
API = "https://api.sume.com/v1"
HEAD = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
@st.cache_data(ttl=600)
def models():
r = requests.get(f"{API}/images/models", headers=HEAD, timeout=30)
r.raise_for_status()
return {m["name"]: m["id"] for m in r.json()["data"]}
st.title("Sume image test bench")
choices = models()
name = st.selectbox("Model", list(choices))
prompt = st.text_area("Prompt")
if st.button("Generate") and prompt:
with st.spinner("Generating..."):
r = requests.post(f"{API}/images", headers=HEAD, timeout=60,
json={"model": choices[name], "prompt": prompt})
if r.status_code == 200:
body = r.json()
st.image(body["data"][0]["url"])
st.caption(f"Billed: ${body['usage']['cost']}")
elif r.status_code == 202:
st.info("Still running: " + r.json()["data"]["status_url"])
else:
st.error(r.text)What should I add before sharing it?
Add a spend cap before anyone else uses it. Each button press is a paid generation, and Sume bills completed ones in full. A simple counter in st.session_state that stops at a dollar total is enough for a demo; see the usage-cost budget guard for a version that reads usage.cost.
Streamlit runs your script server-side, so the key stays off the browser, but anyone with the URL can spend your balance. Put the app behind a login or keep it local.
Why read the catalog instead of hard-coding models?
The catalog is the source of truth for what a key can call, and it lists per-model limits such as the n range and reference count. Reading it at startup means a new model shows up in the picker without a code change, and a retired id disappears instead of failing. The sume/auto value is not a catalog row, so add it to the picker yourself if you want Sume to choose the family.
Sources
Related posts
More in Developers
- Sume STT metadata: tag a transcript job with your own ids
The metadata field on a Sume STT request is stored with the job and is not sent to the speech provider. Use it to tie transcripts to your records.
- Sume STT sentence segmentation fails closed when no words are timed
If the speech provider returns no timed words, a Sume STT request with segmentation returns a typed error instead of guessed sentences. Plan for it.
- Test a Sume STT webhook locally: webhook_url must be public HTTPS
Sume rejects localhost, private-network and non-HTTPS webhook_url values. Put a tunnel in front of your dev server, or poll while you build.
- Authenticate the Sume CLI on a CI runner without a browser login
On CI, skip sume login: install the CLI, run sume auth setup with an API key from a secret, and confirm with sume auth status before any job step.
Written by Sume