What it is
reqwest is the standard high-level HTTP client for Rust, with async and blocking APIs, JSON support, connection pooling, cookies and proxies.
Build a Client once and reuse it. Requests are built fluently and awaited; the json feature integrates with Serde for both request and response bodies.
Installation
cargo add reqwest --features jsonGetting started
The smallest useful thing you can do with it, and what each part means.
rust
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(10))
.build()?;
// Deserialises straight into your type.
let book: Book = client
.get("https://api.example.com/books/42")
.header("Authorization", format!("Bearer {token}"))
.send()
.await?
.error_for_status()? // turn 4xx/5xx into an Err
.json()
.await?;
let created: Book = client
.post("https://api.example.com/books")
.json(&new_book)
.send().await?.error_for_status()?.json().await?;Advanced usage
Where the library earns its place over a simpler alternative.
rust
use futures::stream::{self, StreamExt};
// Bounded concurrency — do not open 10,000 sockets at once.
let results: Vec<_> = stream::iter(urls)
.map(|url| {
let client = client.clone(); // cheap: an Arc internally
async move { client.get(url).send().await?.text().await }
})
.buffer_unordered(16)
.collect()
.await;
// Simple backoff for idempotent requests.
let mut delay = Duration::from_millis(200);
for attempt in 0..3 {
match client.get(&url).send().await {
Ok(r) if r.status().is_success() => break,
_ if attempt < 2 => { tokio::time::sleep(delay).await; delay *= 2; }
other => return other.map(|_| ()).map_err(Into::into),
}
}Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- error decoding response body
- The response is not the JSON you expected, often an HTML error page. Call error_for_status first and log the body when parsing fails.
- Too many open files
- Unbounded concurrent requests. Cap them with buffer_unordered or a semaphore.
Best practices
- Create one Client and clone it; a new Client per request discards the connection pool.
- Always set a timeout — the default is unbounded.
- Call error_for_status so HTTP error codes become Rust errors.
- Use buffer_unordered to bound concurrency rather than spawning unlimited tasks.
Background
Why it exists, and what it was reacting to.
Built by Sean McArthur on top of his Hyper library, reqwest provides the ergonomic layer Hyper deliberately omits, and is the default choice for making HTTP requests from Rust.
