Gradio video generation app: Sume API key in a Space secret

Build a Gradio video generation app on the Sume API: the key stays in a Hugging Face Space secret, a generator polls the job, and gr.Video plays it.

5 min readSume
All posts

To build a Gradio video generation app on the Sume API, read the key from an environment variable (a Hugging Face Space secret once hosted), write a generator that POSTs the prompt to https://api.sume.com/v1/videos and yields the job's status while it polls, then download the MP4 to a temp file and return its path to a gr.Video output.

Sume has no Gradio package or plugin; the app makes plain HTTPS calls with Requests. Sume facts come from Video Generation, Jobs and results, and Authentication; Gradio and Hugging Face facts come from Gradio's quickstart, queuing, streaming outputs, sharing, and file access guides and Hugging Face's Spaces overview, all read on 2026-09-27. The submit, poll, and download loop is explained step by step in Text-to-video API in Python.

What does the Gradio app look like?

One generator function and a Blocks layout, on Python 3.10 or later with pip install --upgrade gradio. Gradio accepts a generator the same way as a regular function and shows the sequence of values it yields, so the status box changes while the job runs.

The function downloads the MP4 because gr.Video displays a file path, and an unsigned_urls entry is an API route that the docs call with your key:

import os, tempfile, time, requests
import gradio as gr

AUTH = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}

def make_video(prompt):
    r = requests.post("https://api.sume.com/v1/videos", headers=AUTH, timeout=30,
                      json={"model": "sume/auto", "prompt": prompt})
    r.raise_for_status()
    job, deadline = r.json(), time.monotonic() + 20 * 60
    while job["status"] not in ("completed", "failed", "cancelled") and time.monotonic() < deadline:
        yield f"{job['id']}: {job['status']}", None
        time.sleep(30)
        job = requests.get(job["polling_url"], headers=AUTH, timeout=30).json()
    if job["status"] != "completed":
        yield f"{job['id']}: {job['status']} {job.get('error', '')}", None
        return
    path = os.path.join(tempfile.mkdtemp(), "video.mp4")
    with open(path, "wb") as f:
        f.write(requests.get(job["unsigned_urls"][0], headers=AUTH, timeout=300).content)
    yield f"{job['id']}: completed", path
with gr.Blocks() as demo:
    prompt, status, video = gr.Textbox(label="Prompt"), gr.Textbox(label="Status"), gr.Video()
    gr.Button("Generate").click(make_video, prompt, [status, video], concurrency_limit=4)
demo.launch()

Where does the Sume API key live?

In the server's environment, never in the page. Gradio's docs note that with a share link, all computation keeps running on your own computer while people use the app from their browsers, and Sume's docs say to keep API keys on trusted servers and out of frontend JavaScript.

  • On Hugging Face Spaces, add SUME_API_KEY as a secret in the Space's settings. Secrets are private, can't be read back from the settings page once set, and aren't added to Spaces duplicated from yours. Variables, by contrast, are public and are copied.
  • Both reach a Gradio app as environment variables, so the same os.environ lookup works in the Space and on your own machine once you export the key there.
  • Don't hard-code it. Hugging Face warns Space owners when its Secrets Scanner finds hard-coded secrets, and a key that shows up in logs or chat history should be rotated.

Who can spend Sume credits through the app?

Anyone who can reach it: every click submits a paid job on your key. Sume's docs say to validate user input and enforce your own authorization before forwarding requests.

  • A public Space lets anyone view the source and use the running app. A protected Space hides the code, but the app stays public; a private Space is open only to the owner and collaborators.
  • Share links are publicly accessible and expire after 1 week.
  • demo.launch(auth=(username, password)) puts a login in front of the app. Gradio calls this a basic layer of access control, without rate limiting, and a gr.LoginButton alone restricts no one.
  • Each event is also an API endpoint named after its function by default, so the button isn't the only way in. Gradio suggests rate limits, by IP address or Hugging Face username, for public apps.
  • The finished MP4 goes into Gradio's cache, and any file in the cache is available by URL to all users of the running app.

Which Gradio settings matter for long video jobs?

Video generation typically takes 30 seconds to several minutes, and the docs suggest polling about every 30 seconds. The code's 20-minute deadline ends the wait, not the job: a client-side deadline does not cancel it, and it keeps running and still bills, so the status line shows the job id for picking it up later. Video job concurrency and queueing covers how many Sume jobs run at once.

Three Gradio settings decide how the wait feels for users:

From Gradio's Button, Video, and file access docs and Sume's Video Generation, read 2026-09-27.
SettingGradio's docsFor a Sume app
concurrency_limit on .click()Default: 1 at a time per eventRaise it, so one user's long job doesn't queue everyone else. Sume applies its own per-workspace job limits.
gr.Video outputExpects a str or Path file pathReturn the downloaded MP4's path from a temp directory; Gradio moves it into its cache.
share in launch()Default: FalseLeave it off for anything that spends money; host on a Space with a secret instead.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume