How to make calls to an API using Rust

Key takeaways:

  • Rust's memory safety and performance make it suitable for building secure web applications and backend services, especially with frameworks like rocket and actix.

  • Tokio is a popular asynchronous runtime in Rust that enables non-blocking I/O operations, allowing applications to perform other tasks while waiting for API responses.

  • The reqwest crate simplifies making http requests in Rust and is designed to work seamlessly with Tokio’s asynchronous model.

  • By using the async keyword, functions can operate asynchronously, improving efficiency during API calls.

  • Rust’s Result enum is used to manage the outcome of API calls, encapsulating either a successful response or an error, enhancing reliability in handling API interactions.

Rust is a systems programming language that boasts its memory safety and performance. These features apply to web development as well. Rust enables developers to build secure and efficient web applications and backend services.

With frameworks like rocket and actix, creating web servers and handling http requests become intuitive and efficient.

In this Answer, we'll have a look at how we can use one of the most popular asynchronous runtimes for Rust for writing efficient and reliable asynchronous code. The runtime, Tokio, provides a powerful foundation for building high-performance, concurrent application, including those that make calls to APIs.

The tokio crate

Let's take a look at how using Tokio as a runtime for making calls to an API can be advantageous in our scenario:

  • Tokio's asynchronous model allows for non-blocking I/O operations. This means that our application can continue to perform other tasks while waiting for API responses.

  • Furthermore, Tokio provides various utilities and abstractions for handling asynchronous tasks. These tools streamline the process of writing asynchronous code, making it more readable and maintainable.

Tokio's asynchronous runtime is compatible with other client libraries that integrate with it seamlessly. Many popular Rust crates such as reqwest, surf and hyper, offer Tokio compatible http clients that simplify the process of making asynchronous requests.

Let's use the reqwest crate for making requests to API and see how Tokio's asynchronous runtime is able to handle the call to the API.

To make API calls, we'll use the reqwest crate for http requests and the tokio crate for asynchronous programming. Add these dependencies to your Cargo.toml file.

Making http requests

Let's take a look at the cart that we can use to make http requests. Making the http requests is time consuming and may halt the program execution till the request is completed. This is where Tokio comes in with its asynchronous runtime.

We can use the reqwest crate for making htp requests so let's create the function that will do that for us:

async fn async_call(url: &str) -> Result<serde_json::Value, reqwest::Error> {
let response: serde_json::Value = reqwest::get(url)
.await?
.json::<serde_json::Value>()
.await?;
Ok(response)
}

In the code shown above,

  • Line 1: In the function signature, we're using the async keyword that we'll import from the tokio crate. This will allow our function to work asynchronously. Moreover, the function takes as argument, the url that we'll be making a call to. Finally, it'll return a Result enum that either contains the API response or an error, if one occurred.

  • Line 2: Here, we're using the get() method for making the call to the url. It returns a Value that contains the API's response.

  • Line 3: We're using the await keyword to halt program execution here for the API response.

  • Lines 4–5: The Value() method here converts the response to a json value and we're again using the await keyword because this is also an asynchronous method.

  • Line 6: Finally, we're returning the API's response wrapped in the Result enum's Ok variant.

Now that we've created the function to make the API call, let's create the main() function that will handle the function to make the API call.

Code example to make a call to an api

Here's the code for the main() function:

async fn async_call(url: &str) -> Result<serde_json::Value, reqwest::Error> {
let response: serde_json::Value = reqwest::get(url)
.await?
.json::<serde_json::Value>()
.await?;
Ok(response)
}
#[tokio::main]
async fn main() {
let api_url: &str = "https://catfact.ninja/fact";
let my_res = async_call(api_url).await;
match my_res {
Ok(r) => {
println!("{:?}", r);
},
Err(_) => {
println!("An error has occured!");
}
}
}

In the code shown above,

  • Line 10: In the function signature, we've made the function asynchronous because of the async_call() function.

  • Line 12: Next up, we're calling the async_call() function with the url that we want to use.

  • Lines 13–20: Finally, we're using a match statement to print the response if the Ok variant was returned and the error if the Err variant was returned.

Frequently asked questions

Haven’t found what you were looking for? Contact Us


How do I send data with a POST request?

We can send data with a POST request in the following way:

let client = reqwest::Client::new();
let res = client.post(url)
    .form(&data)
    .send()
    .await?;

Is API testing hard?

API testing can be straightforward for simple cases, it may require more effort for complex systems. With the right tools and knowledge, it can be manageable.


How can we parse json responses in Rust?

Use the serde_json crate to deserialize json responses into Rust data structures.


Free Resources

Copyright ©2025 Educative, Inc. All rights reserved