Verify a Sume video download against checksum_sha256 in Python

Stream a finished Sume artifact to disk, hash it with hashlib, and compare to checksum_sha256 from the job result. Skips cleanly when the field is null.

5 min readSume
All posts

Fetch GET /v1/jobs/{id}/result, read data.result.artifacts[], stream each url to disk while feeding the bytes to hashlib.sha256, then compare the hex digest with the artifact's checksum_sha256. The field is nullable, so a null value means there is nothing to compare and you should log that instead of failing.

What the result gives you

The OpenAPI schema describes checksum_sha256 as a nullable string on each public artifact. It does not spell out the encoding, so the script below compares lowercase hex and prints both values on a mismatch. If your own jobs return another encoding, that mismatch output will show it immediately.

Artifact fields used here (read 2026-10-04)
FieldType in the schemaUse
urlstring, Sume CDN URLDownload source. The path is opaque; do not parse it.
size_bytesinteger or nullCheap length check before hashing.
checksum_sha256string or nullIntegrity check after download.
content_typestring or nullPick a file extension.

The script

The script uses only the standard library. The blocking calls run in a worker thread through asyncio.to_thread, and asyncio.run is the entry point.

import asyncio, hashlib, json, os, sys, urllib.request

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

def api_get(path):
    req = urllib.request.Request(BASE + path, headers={"x-api-key": os.environ["SUME_API_KEY"]})
    with urllib.request.urlopen(req, timeout=30) as r:
        return json.load(r)

def download(url, dest):
    h, n = hashlib.sha256(), 0
    with urllib.request.urlopen(url, timeout=120) as r, open(dest, "wb") as f:
        for chunk in iter(lambda: r.read(1 << 20), b""):
            f.write(chunk); h.update(chunk); n += len(chunk)
    return h.hexdigest(), n

async def main(job_id):
    result = await asyncio.to_thread(api_get, f"/jobs/{job_id}/result")
    for i, art in enumerate(result["data"]["result"]["artifacts"]):
        digest, n = await asyncio.to_thread(download, art["url"], f"{job_id}-{i}.bin")
        want = art.get("checksum_sha256")
        if want is None:
            print(f"{art['id']}: no checksum published, {n} bytes")
        elif want.lower() != digest:
            sys.exit(f"{art['id']}: mismatch want={want} got={digest}")
        else:
            print(f"{art['id']}: ok, {n} bytes")

asyncio.run(main(sys.argv[1]))

Running it

Run SUME_API_KEY=... python verify.py job_... once the job's terminal flag is true and result_ready is true. A 409 job_not_completed from the result route means the job is not finished yet; see when to stop polling and fetch.

When the digest does not match

Delete the partial file and retry the download once; a second mismatch is worth a support ticket with the job id and x-sume-request-id. Do not resubmit the paid job, because the output already exists. The standard-library hashlib.sha256 is documented in the Python docs.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume