reqwest timeout versus read_timeout for Sume polls and downloads

reqwest has no timeout by default. timeout() is a total deadline including the body; read_timeout() resets per read. Which to use for Sume status polls.

5 min readSume
All posts

Short answer

A reqwest Client has no timeout unless you set one. According to the ClientBuilder docs, timeout() is a total deadline from the start of connecting until the response body has finished, while read_timeout() applies to each read and resets after a successful read. Use timeout() for short Sume status calls and read_timeout() for large result downloads.

Three settings, three questions

Each builder method answers a different failure. connect_timeout covers only the connect phase. timeout bounds the whole request including the body. read_timeout detects a stalled connection when the size is not known beforehand, because a transfer that keeps producing bytes never trips it.

All three default to no timeout, so a hung peer can block a task forever unless you configure them.

reqwest ClientBuilder timeouts, per docs.rs (read 2026-10-03)
MethodApplies toDefault
timeout()Connect through the end of the response body; a total deadlineNone
connect_timeout()Only the connect phaseNone
read_timeout()Each read; resets after a successful readNone

Mapping them onto Sume calls

A status read is a small JSON reply, so a total timeout of tens of seconds is generous and bounds every poll. Sume's sync mode can hold a request open for up to 30 seconds, so if you use it, your total timeout must be longer than that. The simpler route is async: submit, get a 202 and a job id, then poll GET /v1/jobs/:id/status, honoring next_poll_after_seconds, until terminal is true.

Downloading a result file is different. A video can be large, and a total deadline would cut a healthy slow transfer. Build a second client for downloads with a connect_timeout and a read_timeout and no total timeout.

use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let client = reqwest::Client::builder()
        .connect_timeout(Duration::from_secs(5))
        .timeout(Duration::from_secs(30))
        .build()?;
    let res = client.get("https://api.sume.com/v1/health").send().await?;
    println!("{}", res.status());
    Ok(())
}

What a timeout does to a job

A client timeout stops your wait, not the work. If a submit times out locally, the request may already have been accepted, and the generation is billed whether or not you read the reply. Retry the submit with the same Idempotency-Key so a replay returns the original job, and never resubmit as a new request. If you need to stop a job, cancel it, which works only before generation starts; afterward the API answers 409 job_generation_already_started.

Rate limits and retries

Reads are cheap: each key has a read budget forty times its write budget per minute, and responses carry ratelimit-remaining and retry-after on a 429. A poller that follows next_poll_after_seconds stays far below it. Add your own bounded backoff for 429 and 5xx on reads, and for writes only when you have set an Idempotency-Key, so a replayed submit returns the original job.

Cargo and runtime notes

The sample needs reqwest and tokio in Cargo.toml, with tokio's macros and runtime features enabled. The docs note that connect_timeout needs a tokio runtime with timer support, which the tokio runtime provides. Because the sample calls the public GET /v1/health route, it runs with no key; add the x-api-key header from an environment variable for any route that needs one, and never both headers at once.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume