Actix Web integration for the UniRate API — free, real-time currency exchange rates, conversion, and VAT rates.
Register four JSON endpoints on your Actix Web app in one call, backed by the
official unirate-api client:
| Route | Query params | Returns |
|---|---|---|
GET /rate |
from, to |
Single rate, or every rate for from when to is omitted |
GET /convert |
from, to, amount |
Converted amount |
GET /currencies |
— | List of supported currency codes |
GET /vat |
country (optional) |
VAT for one country, or all countries |
This crate does not re-implement the UniRate HTTP client. It depends on the
published unirate-api crate for the
Client, and only adds the Actix Web wiring — so client parity lives in one
place.
[dependencies]
unirate-actix = "0.1"
unirate-api = "0.1"
actix-web = "4"use actix_web::{web, App, HttpServer};
use unirate_actix::configure;
use unirate_api::Client;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let data = web::Data::new(Client::new(std::env::var("UNIRATE_API_KEY").unwrap()));
HttpServer::new(move || {
App::new()
.app_data(data.clone())
.configure(configure)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}Then:
curl 'http://127.0.0.1:8080/rate?from=USD&to=EUR'
# {"from":"USD","to":"EUR","rate":0.9321}
curl 'http://127.0.0.1:8080/convert?from=USD&to=EUR&amount=100'
# {"from":"USD","to":"EUR","amount":100.0,"result":93.21}
curl 'http://127.0.0.1:8080/currencies'
# {"currencies":["USD","EUR","GBP", ...]}
curl 'http://127.0.0.1:8080/vat?country=DE'
# {"country":"DE","vat_data":{"country_code":"DE","country_name":"Germany","vat_rate":19.0}}A full runnable example lives in examples/server.rs:
UNIRATE_API_KEY=your-key cargo run --example serverGet a free API key at https://unirateapi.com.
Registers the four routes above on an Actix
ServiceConfig.
Pass it to App::configure.
The handlers read the Client from
web::Data<Client>,
so register one with App::app_data — a single connection-pooled client is
shared across all requests.
Convenience wrapper that mounts the routes under a path prefix:
use actix_web::{web, App};
use unirate_actix::scope;
use unirate_api::Client;
let data = web::Data::new(Client::new("your-api-key"));
let app = App::new()
.app_data(data)
.service(scope("/currency"));
// now GET /currency/rate, /currency/convert, .../rate—from(defaults toUSD),to(optional; omit to get all rates forfrom). Currency codes are uppercased before the upstream call./convert—from(defaults toUSD),to(required),amount(defaults to1). A missingtoyields400 Bad Request./currencies— no parameters./vat—country(optional ISO-3166 alpha-2; omit to get all countries).
Upstream UniRate errors are mapped to the matching HTTP status with a JSON body of the form:
{ "error": { "status": 401, "message": "Missing or invalid API key" } }| Upstream condition | HTTP status | Message |
|---|---|---|
| Invalid request parameters (400) | 400 Bad Request |
Invalid request parameters |
| Missing / invalid API key (401) | 401 Unauthorized |
Missing or invalid API key |
| Pro-gated endpoint on free tier (403) | 403 Forbidden |
Endpoint requires a Pro subscription |
| Currency not found (404) | 404 Not Found |
Currency not found or no data available |
| Rate limit exceeded (429) | 429 Too Many Requests |
Rate limit exceeded |
| Service unavailable (503) | 503 Service Unavailable |
Service unavailable |
| Other upstream status | that status | upstream body |
| Transport / decode failure | 502 Bad Gateway |
Failed to reach the UniRate API |
A missing required query parameter (e.g. to on /convert) is rejected by
Actix's Query extractor with 400 Bad Request before the upstream is called.
Free-tier keys are rate limited by UniRate; a 429 from upstream surfaces as
429 Too Many Requests. Historical and time-series data are Pro-gated and
return 403 Forbidden on the free tier — this crate only exposes the free-tier
routes (/rate, /convert, /currencies, /vat).
unirate-api— the underlying Rust clientunirate-axum— the same integration for Axum- Official clients for Python, Node/TS, Go, Java, Ruby, PHP, .NET, and Swift
MIT © 2026 Unirate Team. See LICENSE.