Skip to content
Select theme

How to set a timeout on a request

Short answer: reqwest sets no timeout by default — a request waits for as long as the server takes. Put a deadline on every request with Client::builder().timeout(Duration::from_secs(2)), or on one request with .timeout(...) on the request builder.

This picks up the fetch project from How to make a GET request. The code below needs two crates, and no features beyond reqwest’s defaults:

Cargo.toml
[dependencies]
reqwest = "0.13.5"
tokio = { version = "1.53.3", features = ["macros", "rt-multi-thread"] }

If serde is still in that file from the POST article, leave it; nothing here uses it.

Client::new() and the free reqwest::get build a client with no timeout set. Nothing in the request expires: if the server accepts the connection and then goes quiet, send().await waits for as long as it takes. The process looks hung, because it is.

ClientBuilder::timeout covers the whole request — connecting, waiting for the response headers, and reading the body. httpbin’s /delay/10 waits ten seconds before it answers, so the deadline is easy to watch:

cargo run prints (cargo’s own Compiling and Finished lines are not shown):

reqwest::Error { kind: Request, url: "https://httpbin.org/delay/10", source: TimedOut }
is_timeout: true

A timeout arrives as a reqwest::Error, like every other failure from send(), and ? propagates it the same way. is_timeout() tells it apart from the rest when the reaction differs — a timeout is worth another attempt, a malformed URL is not. Retrying properly is a later article’s subject.

The request builder carries a timeout of its own, and it replaces the client’s for that request. The client here allows thirty seconds; the request gives up after two:

Same error as above, two seconds in. reqwest::get(url).timeout(...) behaves the same way, because the free function hands back the same builder.

Two narrower deadlines exist for the cases a total timeout handles badly. When the delay sits in the connection rather than the response, connect_timeout bounds that phase alone. And a body that stalls halfway through is not a slow request but a dead one: read_timeout bounds each read and resets after a successful one, so a slow trickle of bytes never trips it.

The next article in this chapter reads the headers off the response.