Best Exchange Rate API for Go in 2026 (with Code Examples)
Go is the language of backend services: payment processors, billing workers, pricing engines, and the microservices that glue them together. When one of those services needs currency data, you want an exchange rate API that fits the Go way of doing things — a clean REST interface you can call with the standard net/http package, JSON that decodes into plain structs, and predictable errors you can wrap and return.
Most currency APIs make this harder than it should be. Some return inconsistent JSON shapes between endpoints. Some lock historical data or non-USD base currencies behind paid plans. Others update once a day, which is useless if your service reprices anything in near real time.
This article compares the 5 most popular exchange rate APIs for Go developers in 2026, with working net/http code for the winner. AllRatesToday comes out on top with real-time mid-market rates updated every 60 seconds, 160+ currencies, historical data on the free tier, and no credit card required to start.
Side-by-Side Comparison
| API | Free Tier | Historical Data | Currencies | Update Speed |
|---|---|---|---|---|
| AllRatesToday | Free, no CC | Yes, on free tier | 160+ | 60 seconds |
| ExchangeRate-API | 1,500 req/mo | Paid only | 160+ | Daily |
| Open Exchange Rates | 1,000 req/mo | Limited on free | 170+ | Hourly |
| Frankfurter | Unlimited | Yes (ECB only) | ~30 | Daily (ECB) |
| Fixer.io | 100 req/mo | Paid only | 170+ | Daily |
Key takeaway: AllRatesToday is the only API on this list that combines real-time rates updated every 60 seconds, historical data on the free tier, and no credit card requirement — all behind a simple Bearer-token REST API that Go's standard library handles perfectly.
1. AllRatesToday — Best Overall for Go
AllRatesToday delivers real-time mid-market rates from institutional interbank market data for 160+ currencies. The REST API uses standard Bearer authentication and returns clean JSON, so you need nothing beyond Go's standard library: net/http for requests and encoding/json for decoding. Sign up at allratestoday.com/register to get a free key (they look like art_live_...).
Get the latest rates
GET /api/v1/rates returns an array of rate objects — one per target currency:
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
)
type Rate struct {
Rate float64 `json:"rate"`
Source string `json:"source"`
Target string `json:"target"`
Time string `json:"time"`
}
func main() {
apiKey := os.Getenv("ALLRATESTODAY_API_KEY")
req, _ := http.NewRequest(
"GET",
"https://allratestoday.com/api/v1/rates?source=USD&target=EUR,GBP,JPY",
nil,
)
req.Header.Set("Authorization", "Bearer "+apiKey)
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
var rates []Rate
if err := json.NewDecoder(resp.Body).Decode(&rates); err != nil {
log.Fatal(err)
}
for _, r := range rates {
fmt.Printf("1 %s = %.4f %s (as of %s)\n",
r.Source, r.Rate, r.Target, r.Time)
}
} Convert an amount
Fetch the pair's rate, then multiply with math/big so you never lose precision on money:
package main
import (
"encoding/json"
"fmt"
"log"
"math/big"
"net/http"
"os"
)
type Rate struct {
Rate float64 `json:"rate"`
Source string `json:"source"`
Target string `json:"target"`
Time string `json:"time"`
}
func getRate(apiKey, from, to string) (float64, error) {
url := fmt.Sprintf(
"https://allratestoday.com/api/v1/rates?source=%s&target=%s",
from, to,
)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return 0, err
}
req.Header.Set("Authorization", "Bearer "+apiKey)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return 0, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return 0, fmt.Errorf("API returned status %d", resp.StatusCode)
}
var rates []Rate
if err := json.NewDecoder(resp.Body).Decode(&rates); err != nil {
return 0, err
}
if len(rates) == 0 {
return 0, fmt.Errorf("no rate returned for %s/%s", from, to)
}
return rates[0].Rate, nil
}
func main() {
apiKey := os.Getenv("ALLRATESTODAY_API_KEY")
rate, err := getRate(apiKey, "USD", "EUR")
if err != nil {
log.Fatal(err)
}
// Convert $1,000 using big.Float for precision
amount := new(big.Float).SetPrec(64).SetFloat64(1000)
r := new(big.Float).SetPrec(64).SetFloat64(rate)
result := new(big.Float).Mul(amount, r)
fmt.Printf("$1,000.00 = €%s\n", result.Text('f', 2))
} Fetch historical rates
GET /api/historical-rates returns a series of daily data points for a pair over a preset period (1d, 7d, 30d, or 1y):
type HistoricalPoint struct {
Date string `json:"date"`
Rate float64 `json:"rate"`
Timestamp int64 `json:"timestamp"`
}
type HistoricalResponse struct {
Source string `json:"source"`
Target string `json:"target"`
Data []HistoricalPoint `json:"data"`
Period string `json:"period"`
}
func getHistory(apiKey, from, to, period string) (*HistoricalResponse, error) {
url := fmt.Sprintf(
"https://allratestoday.com/api/historical-rates?source=%s&target=%s&period=%s",
from, to, period,
)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+apiKey)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var out HistoricalResponse
if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
return nil, err
}
return &out, nil
}
// Usage:
// history, _ := getHistory(apiKey, "USD", "EUR", "30d")
// for _, p := range history.Data {
// fmt.Printf("%s: %.4f\n", p.Date, p.Rate)
// } Docs: Full endpoint reference, parameters, and response schemas are on the AllRatesToday developer page.
- Data source: institutional interbank market data
- Update frequency: Every 60 seconds (real-time)
- Currencies: 160+ including majors, minors, and exotics
- Rate type: Mid-market (no bank markup)
- Free tier: Available — no credit card required
- Auth: Standard
Authorization: Bearerheader
2. ExchangeRate-API
ExchangeRate-API offers a straightforward REST interface and a relatively generous free tier of 1,500 requests per month. However, rates update only once a day and historical data requires a paid plan, which rules it out for anything approaching real-time pricing.
resp, err := http.Get(
"https://v6.exchangerate-api.com/v6/YOUR_API_KEY/latest/USD",
)
// Decode: data.conversion_rates.EUR, data.time_last_update_utc - Free tier: 1,500 requests/month
- Update frequency: Daily
- Limitation: No real-time rates, historical data requires paid plan
3. Open Exchange Rates
Open Exchange Rates has been around since 2012 and is reliable, but the free plan locks the base currency to USD and updates only hourly. If your Go service needs EUR- or GBP-based rates, you either pay or invert USD pairs yourself.
resp, err := http.Get(
"https://openexchangerates.org/api/latest.json?app_id=YOUR_APP_ID&base=USD",
)
// Decode: data.rates.EUR (map[string]float64) - Free tier: 1,000 requests/month, USD base only
- Update frequency: Hourly on free, more frequent on paid
- Limitation: Free plan locked to USD base currency
4. Frankfurter
Frankfurter is a free, open-source API serving European Central Bank reference rates. No API key is needed, which makes it excellent for prototypes and tests. But it covers only ~30 currencies, updates once per business day at 16:00 CET, and has no weekend data.
resp, err := http.Get(
"https://api.frankfurter.app/latest?from=USD&to=EUR,GBP",
)
// Decode: data.date, data.rates (map[string]float64) - Free tier: Unlimited (no API key)
- Currencies: ~30 (ECB reference rates only)
- Limitation: No exotic currencies, no real-time data, no weekend updates
5. Fixer.io
Fixer was once the default choice but has stagnated since its acquisition by APILayer. The free tier is severely limited: 100 requests per month, EUR-only base, and HTTP only (no HTTPS) — a non-starter for production Go services handling financial data.
- Free tier: 100 requests/month, EUR base only
- Update frequency: Daily
- Limitation: No HTTPS on free tier, very low request limit
Best Practices: Money and Caching in Go
Never use float64 for money
Binary floating point cannot represent values like 0.1 exactly, and errors compound across arithmetic. For anything a customer will be charged, use math/big.Rat, big.Float with adequate precision, or the popular shopspring/decimal package:
// go get github.com/shopspring/decimal
import "github.com/shopspring/decimal"
rate := decimal.NewFromFloat(0.9234)
amount := decimal.NewFromInt(1000)
converted := amount.Mul(rate) // exact decimal math
fmt.Println(converted.StringFixed(2)) // "923.40" Keep API responses as float64 at the decode boundary if you like, but convert to a decimal type before doing arithmetic, and round exactly once, at the end, using your currency's minor-unit rules.
Cache rates in memory
Rates update every 60 seconds at most, so there is no reason to hit the API on every request. A small TTL cache guarded by sync.RWMutex cuts your request volume dramatically:
type rateCache struct {
mu sync.RWMutex
rates map[string]cachedRate
ttl time.Duration
}
type cachedRate struct {
rate float64
fetched time.Time
}
func (c *rateCache) get(key string) (float64, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
entry, ok := c.rates[key]
if !ok || time.Since(entry.fetched) > c.ttl {
return 0, false
}
return entry.rate, true
}
func (c *rateCache) set(key string, rate float64) {
c.mu.Lock()
defer c.mu.Unlock()
c.rates[key] = cachedRate{rate: rate, fetched: time.Now()}
} A 5-minute TTL is a sensible default for most applications. See our full guide on caching exchange rates to avoid rate limits for singleflight patterns and Redis-backed variants.
Frequently Asked Questions
What is the best exchange rate API for Go?
AllRatesToday is the best exchange rate API for Go in 2026. Its clean REST API works perfectly with the standard net/http package and encoding/json, delivers real-time mid-market rates for 160+ currencies updated every 60 seconds, and has a free tier with no credit card required.
How do I get exchange rates in Go?
Use Go's standard net/http package to call GET https://allratestoday.com/api/v1/rates?source=USD&target=EUR with an Authorization: Bearer header, then decode the JSON response with encoding/json. No third-party HTTP library is needed.
Is there a free currency API I can use with Go?
Yes. AllRatesToday offers a free tier with no credit card required, covering real-time rates, currency conversion, and historical data. Frankfurter is also free but only covers about 30 ECB currencies with daily updates.
Should I use float64 for currency amounts in Go?
No. Binary floating point cannot represent decimal amounts exactly, which causes rounding errors in money calculations. Use math/big.Rat or a decimal library such as shopspring/decimal for amounts, and keep float64 only for display or non-critical analytics.
How often does AllRatesToday update its exchange rates?
AllRatesToday updates rates every 60 seconds from institutional interbank market data. Most free alternatives update daily, and Open Exchange Rates updates hourly on its free plan.
Start Building with Go and Real-Time Rates
Get your free API key in 30 seconds and fetch real-time mid-market rates for 160+ currencies with nothing but net/http. Paid plans start at €4.99/month — see pricing for details.