Best Exchange Rate API for Go in 2026 (with Code Examples)

Reviewed by Madhushan, Fintech Developer — August 2026
Source code on a monitor, representing Go development

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.

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

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)

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)

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.

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.

Get Your Free API Key

Related Articles