Runway polls at 5s with jitter; Sume gives next_poll_after_seconds

Runway says poll at 5 seconds or more with jitter and backoff. Sume returns next_poll_after_seconds, so your loop can obey the server instead of guessing.

4 min readSume
All posts

Runway's guidance for manual polling is to poll no faster than every 5 seconds, add jitter, and back off exponentially. Sume's status endpoint adds one thing: when it has an opinion, it tells you the next poll time in next_poll_after_seconds, and the docs say to obey it. When it is absent, use exponential backoff. So the client logic is one line longer, and the guess goes away.

The two polling contracts

Runway's SDK page also lists a waitForTaskOutput helper with a 10 minute default timeout, a TaskFailedError, and the statuses PENDING, SUCCEEDED, CANCELED and FAILED. Sume's job statuses are queued, processing, completed, failed and canceled, and the status response carries booleans so you do not need to compare strings.

Polling contracts, vendor page read 2026-10-05, Sume docs read 2026-10-05
ItemRunwaySume
Interval hint5s or more, with jitter and exponential backoff (client's choice)next_poll_after_seconds in the status payload; backoff when absent
Stop conditionTask status SUCCEEDED, FAILED or CANCELEDterminal: true; fetch the result when result_ready: true
Helper timeoutwaitForTaskOutput default 10 minuteswaitForJob default 20 minutes, 2s poll floor
FailureTaskFailedErrorStatus failed; read the error from GET /v1/jobs/{id}

A Python loop that follows the server

This sketch polls GET /v1/jobs/{id}/status. It uses the server's hint when present and doubles a local delay (capped at 30 seconds) when not. The overall deadline lives in your client: a client timeout does not cancel the job, so store the job id and resume later.

import asyncio, os
import httpx

async def wait_for_job(job_id: str, deadline_s: float = 1200.0) -> dict:
    headers = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
    loop = asyncio.get_running_loop()
    end = loop.time() + deadline_s
    backoff = 2.0
    async with httpx.AsyncClient(base_url="https://api.sume.com/v1", headers=headers) as client:
        while loop.time() < end:
            r = await client.get(f"/jobs/{job_id}/status")
            r.raise_for_status()
            body = r.json()
            s = body.get("data", body)
            if s.get("terminal"):
                return s
            hint = s.get("next_poll_after_seconds")
            delay = float(hint) if hint else backoff
            backoff = min(backoff * 2, 30.0)
            await asyncio.sleep(delay)
    raise TimeoutError(f"{job_id} not terminal after {deadline_s}s")

async def main():
    print(await wait_for_job(os.environ["SUME_JOB_ID"]))

asyncio.run(main())

Rules that carry over from Runway

  • Add jitter if many workers poll the same account, as Runway recommends. Sume's docs say to obey the hint when it is present, so jitter only the fallback backoff.
  • Prefer a webhook for long work and keep polling as a backup. Sume job webhooks are terminal-only (job.completed, job.failed, job.canceled).
  • Never resubmit a paid job because a poll loop timed out. Use the same Idempotency-Key if you must retry the submit itself.

Worked example: how many polls a ten-minute job costs

A fixed 5 second interval over a 10 minute (600 second) wait is 600 / 5 = 120 status reads for one job. Ten jobs polled together would be 1,200 reads. Sume documents that read, status and list endpoints can also have rate limits, and tells you to treat those as poll backpressure, so a tight loop across many jobs is the wrong default. Letting the server's next_poll_after_seconds stretch the interval lowers the count without you picking a number, and a webhook lowers it to near zero.

Whichever vendor you poll, use one shared scheduler for many jobs rather than one loop each. Group ids that share a deadline, space reads with jitter, and stop on the first terminal state. Do not read the result until the status says it is ready.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume