Use reqwest's blocking client to request EUR to JPY, then deserialize the response with serde. The program reads an API key from the environment and prints the exchange rate and a converted euro amount. It also prints the market session and the oldest contributing observation so you can read the rate alongside its timing.
Add reqwest and serde
Put the dependencies in Cargo.toml. The sample uses reqwest 0.12 with the blocking and json features, plus serde 1 with derive. These match the calls and the response struct in the Rust code.
The blocking feature supports reqwest::blocking::Client, and the sample uses json() to deserialize the response. Serde's Deserialize derive applies to Latest, the struct that holds the fields this program reads. Keep these dependencies with the package configuration shown in the snippet before you add the request code.
[package]name = "fx-rate"version = "0.1.0"edition = "2021"[dependencies]reqwest = { version = "0.12", features = ["blocking", "json"] }serde = { version = "1", features = ["derive"] }Request EUR to JPY
Put the client code in src/main.rs. It sends GET https://api.exchangerate.dev/v1/latest/EUR?symbols=JPY and passes the key through bearer_auth. The key goes in the Authorization header as a bearer token.
The latest function takes the base currency, quote currency and key. Its response struct contains rates, market_session and data_updated_at. After the request, error_for_status() turns a non-2xx response into an error before the program attempts to deserialize the JSON.
In main, the program requests EUR as the base and JPY as the quote, reads the JPY entry from rates, and multiplies the sample euro amount by that rate.
use std::collections::HashMap;use std::env;use std::error::Error;use serde::Deserialize;#[derive(Deserialize)]struct Latest { rates: HashMap<String, f64>, market_session: String, data_updated_at: String,}fn latest(base: &str, quote: &str, key: &str) -> Result<Latest, Box<dyn Error>> { let url = format!("https://api.exchangerate.dev/v1/latest/{base}?symbols={quote}"); let response = reqwest::blocking::Client::new() .get(url) .bearer_auth(key) .send()? .error_for_status()?; Ok(response.json()?)}fn main() -> Result<(), Box<dyn Error>> { let key = env::var("EXCHANGERATE_API_KEY")?; let data = latest("EUR", "JPY", &key)?; let rate = data.rates["JPY"]; println!("1 EUR = {rate} JPY ({}, updated {})", data.market_session, data.data_updated_at); println!("1000 EUR = {:.0} JPY", 1000.0 * rate); Ok(())}Set the key and run the program
Set EXCHANGERATE_API_KEY in the environment, then run cargo run. Replace your_key in the shell snippet with your API key. The program reads that variable through env::var and passes its value to the request function.
The environment variable must be available when the program runs. If it is absent, the environment lookup returns an error before the request. Request errors, HTTP status errors and JSON deserialization errors also propagate through the sample's Result return values. The rate and converted amount print after the request and deserialization succeed.
export EXCHANGERATE_API_KEY="your_key"cargo runRead the rate and converted amount
The first output line gives JPY per EUR, followed by market_session and data_updated_at. The second line gives the sample euro amount multiplied by the returned JPY rate. Its formatting prints the converted amount with no decimal places.
The output snippet contains example values. Read your run's rate together with its session and observation time, since those fields belong to the response your program received.
The converted amount uses indicative mid-market figures. Banks, cards and transfer services apply their own spread and fees, so their amounts can differ from this calculation. These rates are not for settlement or trading.
1 EUR = 175.604 JPY (open, updated 2026-10-23T18:00:59Z)1000 EUR = 175604 JPYCheck the observation time
data_updated_at is the oldest contributing observation. The sample prints it beside market_session, so the rate's output includes timing information as well as the numeric quote. The response also has timestamp and effective_at; the sample does not deserialize those fields.
Other response fields are result, base, source, sources, stale, derived_symbols and notice. The sources field has a source per symbol, and stale is an object per symbol. The Latest struct selects the fields needed for this program's calculation and printed output.
Choose access for your request volume
A free key has 5,000 requests a month and 10 requests a minute. Its rates are 15 minutes late. Paid plans are live. The blocking client sends the same request with the key supplied through the environment.
Keyless access allows 10 requests a minute and 100 an hour per IP, with rates 15 minutes late. This sample reads a key and sends a bearer header, so use an API key when you run it as written. The API reference is the related documentation for the endpoint.