Question

How to Make an HTTP Request in Rust

rusthttp

Question

How can I make an HTTP request in Rust? I cannot find support for this in the standard library.

I only need to send a request and inspect the HTTP response status code; I do not need to parse the response body.

It would also be helpful to see how to safely URL-encode query parameters.

For example, the goal is something like:

// Pseudocode
let response = make_request("https://example.com/search?q=rust language");
println!("Status: {}", response.status());

Short Answer

By the end of this page, you will understand how HTTP requests are typically made in Rust, why the standard library does not include a full HTTP client, and how to use a crate such as reqwest to send requests, check status codes, and safely add URL-encoded query parameters.

Concept

Rust's standard library focuses on core building blocks such as strings, collections, file I/O, networking primitives, and concurrency. It does not provide a high-level HTTP client.

That means if you want to make HTTP requests in Rust, you usually use a community crate. The most common beginner-friendly choice is [reqwest], which provides a simple API for sending HTTP requests.

Why this matters

HTTP requests are used everywhere in real programs:

  • calling APIs
  • downloading files
  • checking whether a service is online
  • sending form data
  • authenticating users

A good HTTP client should help you:

  • build URLs safely
  • send requests using methods like GET and POST
  • inspect status codes such as 200 OK or 404 Not Found
  • handle network errors cleanly

Key idea

In Rust, the usual workflow is:

  1. Add an HTTP client crate to Cargo.toml
  2. Send a request
  3. Check the returned Response
  4. Read the status code or body if needed

For URL encoding, you should avoid manually concatenating strings when query values may contain spaces, &, ?, or other special characters. Instead, use helper methods that encode parameters correctly for you.

Mental Model

Think of Rust's standard library as a basic toolbox.

It gives you essentials like:

  • a hammer
  • a screwdriver
  • measuring tape

But an HTTP client is more like a specialized power tool. It is extremely useful, but not part of the smallest core set of tools.

So when you need to talk to a website or API, you reach for an external crate like reqwest.

For query parameters, imagine writing an address on a package. If the address contains spaces or special symbols, you need to format it correctly so the delivery system understands it. URL encoding does that formatting for web addresses.

Take Quiz

Syntax and Examples

The easiest way to make an HTTP request in Rust is with the reqwest crate.

Add the dependency

In Cargo.toml:

[dependencies]
reqwest = { version = "0.12", features = ["blocking"] }

The blocking feature is useful for simple scripts and beginner examples.

Simple GET request

use reqwest::blocking::get;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    let response = get("https://httpbin.org/status/200")?;

    println!("Status: {}", response.status());

    Ok(())
}

What this does

  • get(...) sends a GET request
  • it returns a Response
  • response.status() gives the HTTP status code
  • ? passes errors upward if the request fails

Adding query parameters safely

Instead of building the URL manually, use .query(...):

use reqwest::blocking::Client;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    let client = Client::new();

    let response = client
        .get("https://httpbin.org/get")
        .query(&[("q", "rust language"), ("page", "1")])
        .send()?;

    println!("Status: {}", response.status());

    Ok(())
}

Why .query(...) is better

It automatically URL-encodes values like:

  • spaces
  • punctuation
  • symbols such as &

For example, rust language becomes a safe query value in the final URL.

Manual URL encoding example

If you specifically need to encode values yourself, use the urlencoding crate.

In Cargo.toml:

[dependencies]
urlencoding = "2"

Example:

use urlencoding::encode;

fn main() {
    let term = "rust language & web";
    let encoded = encode(term);

    println!("{}", encoded);
}

This prints an encoded string suitable for placing into a URL query.

Step by Step Execution

Consider this example:

use reqwest::blocking::Client;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    let client = Client::new();

    let response = client
        .get("https://httpbin.org/get")
        .query(&[("q", "rust language")])
        .send()?;

    println!("Status: {}", response.status());

    Ok(())
}

Here is what happens step by step:

  1. Client::new() creates an HTTP client.
  2. .get("https://httpbin.org/get") prepares a GET request.
  3. .query(&[("q", "rust language")]) adds a query parameter.
    • Rust passes the key q
    • Rust passes the value rust language
    • reqwest URL-encodes the value safely
  4. .send()? sends the request over the network.
    • If the network is down or the URL is invalid, an error is returned.
    • Otherwise, you get a Response.
  5. response.status() reads the HTTP status code.
  6. println! prints something like Status: 200 OK.
  7. Ok(()) ends the program successfully.

Important detail

An HTTP error like 404 Not Found is still usually a successful network request. That means:

  • the request itself worked
  • the server responded
  • the status code indicates the result

So you often need to check response.status() explicitly.

Real World Use Cases

HTTP requests in Rust are common in many types of software.

API clients

A Rust service may call another API to fetch:

  • user profiles
  • payment status
  • weather data
  • shipping updates

Health checks

A monitoring script can send a request to a service endpoint and verify that it returns 200 OK.

Command-line tools

A CLI written in Rust might:

  • test whether a URL is reachable
  • verify a deployment endpoint
  • query a web service

Data ingestion

A backend job can request data from a remote server, then process or store it.

Search and filtering

Applications often send query parameters such as:

  • ?q=rust
  • ?page=2
  • ?sort=desc

Using a proper query builder avoids broken URLs and encoding bugs.

Take Quiz

Real Codebase Usage

In real Rust codebases, developers usually avoid one-off string concatenation for URLs and instead use clients and helper methods.

Common patterns

Reuse a Client

Instead of calling a top-level get everywhere, many projects create a Client once and reuse it.

use reqwest::blocking::Client;

fn build_client() -> Client {
    Client::new()
}

This keeps code organized and is often more efficient.

Check status explicitly

if response.status().is_success() {
    println!("Request succeeded");
} else {
    println!("Request failed with status: {}", response.status());
}

Guard clauses for errors

use reqwest::blocking::get;
use std::error::Error;

fn check_url(url: &str) -> Result<(), Box<dyn Error>> {
    let response = get(url)?;

    if !response.status().is_success() {
        return Err(format!("Request failed: {}", response.status()).into());
    }

    Ok(())
}

Separate URL construction from request logic

This makes testing and maintenance easier.

fn search_url() -> &'static str {
    "https://httpbin.org/get"
}

Use query helpers instead of manual formatting

client
    .get(search_url())
    .query(&[("q", "rust language"), ("limit", "10")])
    .send()?;

This reduces encoding mistakes and makes code easier to read.

Common Mistakes

Beginners often run into a few common problems when making HTTP requests in Rust.

1. Expecting HTTP support in the standard library

Rust's standard library does not include a high-level HTTP client.

Incorrect idea

// This kind of API does not exist in std
// let response = std::http::get("https://example.com");

Fix

Add a crate like reqwest in Cargo.toml.

2. Manually building query strings unsafely

Problem

let term = "rust language & web";
let url = format!("https://example.com/search?q={}", term);

This can produce an invalid or misleading URL because special characters are not encoded.

Better

let response = client
    .get("https://example.com/search")
    .query(&[("q", "rust language & web")])
    .send()?;

3. Confusing network errors with HTTP status errors

Problem

Some learners think 404 automatically becomes a Rust error.

Reality

  • network failure: Rust error
  • timeout: Rust error
  • invalid URL: Rust error
  • 404 Not Found: usually still a valid Response

You must inspect the status code.

4. Forgetting to enable the right feature

If you want blocking examples, the blocking feature must be enabled.

reqwest = { version = "0.12", features = ["blocking"] }

5. Ignoring the Result

Broken code

let response = reqwest::blocking::get("https://example.com");
println!("{:?}", response.status());

This fails because get(...) returns a Result<Response, Error>.

Fixed code

let response = reqwest::blocking::get("https://example.com")?;
println!("Status: {}", response.status());

6. Using string concatenation everywhere

Large codebases become harder to maintain if every URL is assembled by hand. Prefer helper functions, query builders, and constants.

Comparisons

Here are some useful comparisons related to making HTTP requests in Rust.

OptionBest forProsCons
std library onlyLow-level networkingNo extra dependenciesNo high-level HTTP client
reqwest::blockingSimple scripts, beginner examplesEasy to read, straightforwardBlocks the current thread
async reqwestAsync apps and serversWorks well in async systemsSlightly more setup
Manual URL string buildingVery simple fixed URLsQuick for tiny casesError-prone for query parameters
.query(...) helperQuery parametersSafe and readableRequires using the client API

get(...) vs Client::new().get(...)

ApproachWhen to useExample
reqwest::blocking::get(...)One simple requestSmall scripts
ClientMultiple requests, reusable setupReal applications

Manual encoding vs automatic encoding

ApproachRecommendationReason
Manual encodingUse only when necessaryEasy to make mistakes
.query(...)PreferredAutomatically encodes values correctly
Take Quiz

Cheat Sheet

[dependencies]
reqwest = { version = "0.12", features = ["blocking"] }

Simple request

let response = reqwest::blocking::get("https://example.com")?;
println!("{}", response.status());

Reusable client

let client = reqwest::blocking::Client::new();
let response = client.get("https://example.com").send()?;

Add query parameters safely

let response = client
    .get("https://example.com/search")
    .query(&[("q", "rust language"), ("page", "1")])
    .send()?;

Check status

if response.status().is_success() {
    println!("OK");
}

Remember

  • reqwest is not part of the standard library
  • get(...) returns Result<Response, Error>
  • 404 is usually not a Rust error by itself
  • use .query(...) instead of manual string concatenation
  • enable blocking if using blocking examples

Optional manual encoding

urlencoding = "2"
let encoded = urlencoding::encode("rust language & web");

FAQ

Why doesn't Rust's standard library include HTTP requests?

Rust keeps the standard library relatively small and focused on core features. High-level HTTP support is usually provided by crates such as reqwest.

What crate should I use for HTTP requests in Rust?

For most beginners and many real projects, reqwest is the most common choice because it is ergonomic and well documented.

How do I check only the HTTP status code in Rust?

Send the request, get the Response, and call response.status().

How do I URL-encode query parameters in Rust?

The safest option is to use .query(...) with reqwest::Client, which encodes parameters for you.

Is 404 Not Found returned as a Rust error?

Usually no. A 404 is often a valid HTTP response, so you need to inspect the status code yourself.

Should I use blocking or async reqwest?

Use blocking for simple scripts and learning. Use async reqwest when working inside an async application.

Can I make a request without parsing the body?

Yes. You can send the request and inspect only response.status() without reading the response text.

Take Quiz

Mini Project

Description

Build a small Rust command-line program that sends an HTTP GET request to a search endpoint, includes user-provided query parameters safely, and prints the HTTP status code. This demonstrates the most common beginner workflow: create a client, attach query parameters, send a request, and inspect the response.

Goal

Create a Rust program that sends a GET request with encoded query parameters and reports whether the request succeeded based on the HTTP status code.

Requirements

  1. Create a Rust project that uses reqwest with the blocking feature.
  2. Send a GET request to https://httpbin.org/get.
  3. Add at least two query parameters using a safe query builder.
  4. Print the full HTTP status code.
  5. Print a success or failure message based on the status.
Take Quiz

Keep learning

Related questions

View more