How can I set a timeout for a request in Reqwest?

Reqwest, the popular HTTP client for Rust, provides multiple timeout options to control request timing behavior. You can set timeouts at both the client level and individual request level to prevent your application from hanging on slow or unresponsive servers.

Basic Request Timeout

Use the timeout() method on a RequestBuilder to set a timeout for individual requests:

use reqwest::Client;
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let client = Client::new();

    let response = client
        .get("https://httpbin.org/delay/5")
        .timeout(Duration::from_secs(10))
        .send()
        .await;

    match response {
        Ok(res) => {
            println!("Status: {}", res.status());
            let body = res.text().await?;
            println!("Body: {}", body);
        }
        Err(err) => {
            if err.is_timeout() {
                println!("Request timed out after 10 seconds");
            } else {
                println!("Request failed: {}", err);
            }
        }
    }

    Ok(())
}

Client-Level Timeouts

Set default timeouts for all requests made by a client using ClientBuilder:

use reqwest::ClientBuilder;
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let client = ClientBuilder::new()
        .timeout(Duration::from_secs(30))        // Total request timeout
        .connect_timeout(Duration::from_secs(5)) // Connection establishment timeout
        .build()?;

    let response = client
        .get("https://httpbin.org/get")
        .send()
        .await?;

    println!("Response: {}", response.text().await?);
    Ok(())
}

Multiple Timeout Types

Reqwest supports different timeout types for fine-grained control:

use reqwest::ClientBuilder;
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let client = ClientBuilder::new()
        .connect_timeout(Duration::from_secs(10))  // Time to establish connection
        .timeout(Duration::from_secs(60))          // Total request timeout
        .read_timeout(Duration::from_secs(30))     // Time to read response data
        .build()?;

    let response = client
        .post("https://httpbin.org/post")
        .json(&serde_json::json!({"key": "value"}))
        .send()
        .await?;

    println!("Posted successfully: {}", response.status());
    Ok(())
}

Override Client Timeouts

Individual requests can override client-level timeouts:

use reqwest::ClientBuilder;
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    // Client with 30-second default timeout
    let client = ClientBuilder::new()
        .timeout(Duration::from_secs(30))
        .build()?;

    // This request uses a shorter 5-second timeout
    let quick_response = client
        .get("https://httpbin.org/delay/2")
        .timeout(Duration::from_secs(5))
        .send()
        .await;

    // This request uses the default 30-second timeout
    let normal_response = client
        .get("https://httpbin.org/delay/10")
        .send()
        .await;

    match quick_response {
        Ok(_) => println!("Quick request succeeded"),
        Err(e) if e.is_timeout() => println!("Quick request timed out"),
        Err(e) => println!("Quick request error: {}", e),
    }

    Ok(())
}

Handling Timeout Errors

Properly handle different types of errors, including timeouts:

use reqwest::{Client, Error};
use std::time::Duration;

async fn make_request_with_retry(url: &str, max_retries: u32) -> Result<String, Error> {
    let client = Client::new();

    for attempt in 1..=max_retries {
        let result = client
            .get(url)
            .timeout(Duration::from_secs(10))
            .send()
            .await;

        match result {
            Ok(response) => return response.text().await,
            Err(err) => {
                if err.is_timeout() {
                    println!("Attempt {} timed out, retrying...", attempt);
                    if attempt == max_retries {
                        return Err(err);
                    }
                    tokio::time::sleep(Duration::from_secs(1)).await;
                } else {
                    return Err(err); // Non-timeout error, don't retry
                }
            }
        }
    }

    unreachable!()
}

#[tokio::main]
async fn main() {
    match make_request_with_retry("https://httpbin.org/delay/15", 3).await {
        Ok(body) => println!("Success: {}", body),
        Err(e) => println!("Failed after retries: {}", e),
    }
}

Timeout Best Practices

  • Connect timeout: 5-10 seconds for most applications
  • Read timeout: 30-60 seconds depending on expected response size
  • Total timeout: Should be longer than read timeout to account for connection time
  • Override when needed: Use request-level timeouts for specific endpoints that need different timing
  • Handle timeout errors gracefully: Implement retry logic for timeout scenarios where appropriate

Cargo.toml Dependencies

Add these dependencies to your Cargo.toml:

[dependencies]
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1.0", features = ["full"] }
serde_json = "1.0"

The timeout functionality covers the entire request lifecycle, including DNS resolution, connection establishment, sending the request, and reading the response. This ensures your application remains responsive even when dealing with slow or unresponsive servers.

Related Questions

Get Started Now

WebScraping.AI provides rotating proxies, Chromium rendering and built-in HTML parser for web scraping
Icon