Official Go client for the UniRate API — free, real-time and historical currency exchange rates plus VAT rates.
- Real-time exchange rates between 170+ currencies (fiat + crypto)
- Historical rates back to 1999
- Time-series ranges up to 5 years
- Currency conversion (current and historical)
- VAT rates for countries worldwide
- Free tier, no credit card required
- Idiomatic Go:
context.Contexton every method, sentinel errors, no external dependencies
- Go 1.21 or newer
go get github.com/UniRate-API/unirate-api-gopackage main
import (
"context"
"fmt"
"log"
"os"
unirate "github.com/UniRate-API/unirate-api-go"
)
func main() {
client := unirate.New(os.Getenv("UNIRATE_API_KEY"))
ctx := context.Background()
rate, err := client.GetRate(ctx, "USD", "EUR")
if err != nil {
log.Fatal(err)
}
fmt.Printf("USD -> EUR: %.4f\n", rate)
euros, err := client.Convert(ctx, 100, "USD", "EUR")
if err != nil {
log.Fatal(err)
}
fmt.Printf("100 USD = %.2f EUR\n", euros)
}Get a free API key at https://unirateapi.com.
client := unirate.New("your-api-key",
unirate.WithTimeout(10 * time.Second), // override default 30s
unirate.WithHTTPClient(customHTTPClient), // inject your own *http.Client (or any HTTPDoer)
unirate.WithBaseURL("https://api.example"), // override base URL (for tests)
)rate, err := client.GetRate(ctx, "USD", "EUR")
// → float64
rates, err := client.GetAllRates(ctx, "USD")
// → map[string]float64
result, err := client.Convert(ctx, 100, "USD", "EUR")
// → float64
codes, err := client.GetSupportedCurrencies(ctx)
// → []stringrate, err := client.GetHistoricalRate(ctx, "2024-01-01", "USD", "EUR")
rates, err := client.GetHistoricalRates(ctx, "2024-01-01", "USD")
amount, err := client.ConvertHistorical(ctx, 100, "USD", "EUR", "2024-01-01")
series, err := client.GetTimeSeries(ctx,
"2024-01-01", "2024-01-07",
1, // amount
"USD", // base
[]string{"EUR", "GBP"}, // pass nil for all
)
limits, err := client.GetHistoricalLimits(ctx)all, err := client.GetVATRates(ctx)
de, err := client.GetVATRate(ctx, "DE")
fmt.Println(de.VATData.VATRate) // 19.0Every method accepts an optional CallOptions struct as a trailing
variadic argument:
client.GetRate(ctx, "USD", "EUR",
unirate.CallOptions{Format: "xml"},
)Non-JSON formats will cause the typed decode to fail — use a custom
*http.Client and build the URL yourself if you need raw XML/CSV bodies.
The client returns sentinel errors wrapped with [fmt.Errorf], so use
[errors.Is] to classify them and [errors.As] to extract HTTP details:
_, err := client.GetRate(ctx, "USD", "ZZZ")
switch {
case errors.Is(err, unirate.ErrAuthentication):
// invalid API key
case errors.Is(err, unirate.ErrInvalidCurrency):
// unknown currency code
case errors.Is(err, unirate.ErrRateLimit):
// back off and retry
case errors.Is(err, unirate.ErrInvalidDate):
// bad date format
default:
var apiErr *unirate.APIError
if errors.As(err, &apiErr) {
// generic HTTP error with apiErr.StatusCode / apiErr.Body,
// including 403 on Pro-gated endpoints
}
}The WithHTTPClient option accepts anything that implements Do(*http.Request) (*http.Response, error), which *http.Client already satisfies. Combined with WithBaseURL, that makes it trivial to point the client at an httptest.Server:
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte(`{"rate": "0.9"}`))
}))
defer srv.Close()
client := unirate.New("test-key",
unirate.WithBaseURL(srv.URL),
unirate.WithHTTPClient(srv.Client()),
)- Currency endpoints: standard rate limits apply
- Historical endpoints: 50 requests/hour on the free tier
- VAT endpoints: 1800 requests/hour on the free tier
go test ./... # mock tests (no network)
UNIRATE_API_KEY=... go test -tags live ./... # live tests (free-tier endpoints)- unirate-api-python (PyPI:
unirate-api) - unirate-api-nodejs (npm:
unirate-api) - unirate-api-swift
MIT — see LICENSE.