uv run a single-file Python script against the Sume API (PEP 723)

A one-file Sume script with inline PEP 723 dependencies runs with uv run and no virtualenv. Submit, poll next_poll_after_seconds, print the result.

4 min readSume
All posts

Put a # /// script block at the top of one Python file, list httpx under dependencies, and run it with uv run script.py. PEP 723 (read 2026-10-10) defines that inline metadata with requires-python and dependencies, and a runner like uv builds the environment on the fly.

That makes a Sume job script one file you can paste into a ticket, a gist or a CI step, with no requirements.txt and no virtualenv to document.

The whole script

The script below submits an image job in async mode with an Idempotency-Key, then polls /v1/jobs/{id}/status. It sleeps for next_poll_after_seconds when the API sends one and for 2 seconds otherwise, stops on terminal, and prints the final status and the result body. The Python here is 3.14 because it uses uuid.uuid7(); change requires-python and use uuid4 if you must stay older.

# /// script
# requires-python = ">=3.14"
# dependencies = ["httpx"]
# ///
import os, sys, time, uuid

import httpx


def main() -> None:
    headers = {"x-api-key": os.environ["SUME_API_KEY"]}
    with httpx.Client(base_url="https://api.sume.com", headers=headers, timeout=30) as c:
        r = c.post(
            "/v1/image-1.0/generate",
            json={"prompt": sys.argv[1], "mode": "async"},
            headers={"Idempotency-Key": str(uuid.uuid7())},
        )
        r.raise_for_status()
        job = r.json()["data"]["request_id"]
        while True:
            s = c.get(f"/v1/jobs/{job}/status").json()["data"]
            if s["terminal"]:
                break
            time.sleep(s["next_poll_after_seconds"] or 2)
        print(s["sume_status"], c.get(f"/v1/jobs/{job}/result").json())


main()

What each part maps to in the Sume docs

Every line of the polling logic follows the Jobs and results page.

Script lines against the docs (docs read 2026-10-10)
Script behaviourSume field or ruleWhy
Sends x-api-keyAuthentication accepts Bearer or x-api-key, never bothBoth headers at once returns 401
Same Idempotency-Key for the submitKey echoed as idempotency_key on the jobLets you find the job after a crash
Stops on terminalStatus field terminalDo not infer done from the status word alone
Sleeps next_poll_after_secondsHint on the status body; back off when absentFewer wasted reads against the read budget

Running it and keeping the key out of the file

Export SUME_API_KEY in your shell and run uv run script.py "a bottle on marble". The script reads the key from the environment only, so the file is safe to commit. If you use a secrets manager, wrap the command in its run helper instead of pasting the key into a shell profile.

Where a single file stops being enough

A practical middle step is to keep the script and add a small JSON file next to it that stores the key for each prompt you submitted. Rerunning the script then resumes the same job instead of paying twice, which is the idempotency contract working for you at the smallest possible scale.

Once you need persistence for idempotency keys, a webhook receiver, or retries across restarts, a script is the wrong shape. The same polling loop belongs in a small module, and the key belongs in a database row. Until then, one file with pinned inline dependencies is the fastest way to get a first paid job through the API and to share a reproducible example with a teammate who has never installed your repo.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume