Packages
unirate_api
0.1.0
Official Elixir client for the UniRate API — free currency exchange rates, historical data, and VAT rates.
Current section
Files
Jump to
Current section
Files
unirate_api
README.md
README.md
# UniRate Elixir Client
Official Elixir client for the [UniRate API](https://unirateapi.com) — free
currency exchange rates, historical data, and VAT rates.
Built on the Erlang/OTP standard library only (`:httpc` for HTTP and the OTP 27
`:json` module for parsing): **zero third-party dependencies**.
## Requirements
- Elixir `~> 1.17`
- **Erlang/OTP 27 or newer** (the `:json` module ships with OTP 27)
## Installation
Add `unirate_api` to your `deps` in `mix.exs`:
```elixir
def deps do
[
{:unirate_api, "~> 0.1.0"}
]
end
```
Then run `mix deps.get`.
## Quick start
```elixir
client = UniRate.Client.new("your-api-key")
# A single exchange rate.
UniRate.Client.get_rate(client, "USD", "EUR")
#=> 0.92
# All rates for a base currency.
UniRate.Client.get_rate(client, "USD")
#=> %{"EUR" => 0.92, "GBP" => 0.79, ...}
# Convert an amount.
UniRate.Client.convert(client, "EUR", from: "USD", amount: 100)
#=> 92.5
# Supported currencies.
UniRate.Client.get_supported_currencies(client)
#=> ["USD", "EUR", "GBP", ...]
# VAT rate for a country.
UniRate.Client.get_vat_rate(client, "DE")
#=> %{"country_code" => "DE", "country_name" => "Germany", "vat_rate" => 19.0}
```
Currency and country codes are upper-cased automatically before being sent.
## Configuration
```elixir
UniRate.Client.new("your-api-key",
timeout: 30_000, # request timeout in milliseconds
base_url: "https://api.unirateapi.com"
)
```
## API
### Current data (free tier)
| Method | Returns |
|---|---|
| `get_rate(client, from \\ "USD", to \\ nil, opts \\ [])` | a number when `to` is set, else a `%{code => rate}` map |
| `convert(client, to, opts \\ [])` | the converted amount (options: `:amount`, `:from`) |
| `get_supported_currencies(client, opts \\ [])` | a list of currency-code strings |
| `get_vat_rates(client, opts \\ [])` | the full VAT response (option: `:country`) |
| `get_vat_rate(client, country, opts \\ [])` | the `vat_data` object for one ISO-3166 alpha-2 country |
### Historical data (Pro plan)
These endpoints require a Pro subscription and return `403` on the free tier
(surfaced as a `UniRate.Error` with `reason: :api` and `status: 403`).
| Method |
|---|
| `get_historical_rate(client, date, opts \\ [])` — options `:amount`, `:from`, `:to` |
| `get_historical_rates(client, date, opts \\ [])` — options `:amount`, `:base` |
| `convert_historical(client, amount, from, to, date, opts \\ [])` |
| `get_time_series(client, start_date, end_date, opts \\ [])` — options `:amount`, `:base`, `:currencies` |
| `get_historical_limits(client, opts \\ [])` |
Every method also accepts `:format` (`"json"` default, or `"xml"` / `"csv"` /
`"tsv"`) and `:callback`. When `:format` is not `"json"`, the raw response body
string is returned instead of a parsed value.
## Error handling
Every failure is a `UniRate.Error` — rescue the whole family with one clause and
narrow on `:reason`:
```elixir
try do
UniRate.Client.get_rate(client, "USD", "ZZZ")
rescue
e in UniRate.Error ->
case e.reason do
:invalid_currency -> IO.puts("unknown currency")
:authentication -> IO.puts("bad API key")
:rate_limit -> IO.puts("slow down")
:api -> IO.puts("API error #{e.status}: #{e.message}")
:transport -> IO.puts("network error: #{e.message}")
_ -> reraise(e, __STACKTRACE__)
end
end
```
| Reason | HTTP | Meaning |
|---|---|---|
| `:invalid_date` | 400 | Invalid request parameters |
| `:authentication` | 401 | Missing or invalid API key |
| `:api` | 403 | Endpoint requires a Pro subscription |
| `:invalid_currency` | 404 | Currency not found or no data available |
| `:rate_limit` | 429 | Rate limit exceeded |
| `:api` | 503 / other | Service unavailable / generic (carries `status` + `body`) |
| `:transport` | — | Network/transport failure or unparseable response |
## Rate limits
The free tier is rate-limited. On HTTP 429 the client raises a `UniRate.Error`
with `reason: :rate_limit`; back off and retry.
## Testing
```bash
mix test # mock tests only (default)
UNIRATE_API_KEY=... mix test --include live # + live free-tier tests
```
Mock tests inject a transport function via the `:http` option, so they never hit
the network.
## Related clients
UniRate ships official clients for many languages — Python, Node/TypeScript,
Go, Rust, Ruby, PHP, Java, Swift, and more. See
[github.com/UniRate-API](https://github.com/UniRate-API).
## License
MIT © 2026 Unirate Team. See [LICENSE](LICENSE).