TutorialsCode

Silver price in Go

Fetch silver prices from goldprice.dev in Go with net/http, decimal arithmetic, caching, and retries for rate limits.

23 Oct 2026 · 5 min read

To get silver prices in Go, send a GET request to https://api.goldprice.dev/v1/spot/XAG-USD-SPOT with your API key in the X-API-Key header. Use net/http for requests and shopspring/decimal to decode price strings into decimals. Silver spot is quoted in US dollars per troy ounce. Free prices are fifteen minutes late; paid plans are live. The client below caches each symbol and retries requests that hit a rate limit.

Get your goldprice.dev API key

Create a free account at goldprice.dev/onboarding and get your API key. Set it in the shell where you will run the program:

Shell
export GOLDPRICE_API_KEY=ga_live_replace_with_your_key_here

Free includes 5,000 calls a month and 10 requests a minute, with no credit card required. Silver spot is available on every plan, in US dollars only. Free prices are fifteen minutes late.

Initialise the module and add the decimal dependency

The standard library handles HTTP requests and JSON decoding. Add shopspring/decimal to read price strings as decimals and avoid binary floating point rounding in price calculations:

shell
go mod init goldprice-demogo get github.com/shopspring/decimal

Write the silver price client

Save this as goldprice.go. The Spot() method caches each symbol for sixty seconds behind a mutex. It makes up to three attempts when requests return 429 and waits longer after each failed attempt.

The response struct decodes price strings into decimal.Decimal. Check IsStale and ComputedAt before using a quote.

The cache controls local requests. It does not change your plan's price delay.

goldprice.go
// go: goldprice.go// curl-equivalent: https://api.goldprice.dev/v1/spot/XAG-USD-SPOTpackage mainimport (	"encoding/json"	"errors"	"fmt"	"net/http"	"os"	"sync"	"time"	"github.com/shopspring/decimal")const (	apiBase = "https://api.goldprice.dev"	timeout = 5 * time.Second	ttl     = 60 * time.Second)// SpotResponse holds the fields we consume from /v1/spot/{symbol}.type SpotResponse struct {	Symbol        string          `json:"symbol"`	QuoteCurrency string          `json:"quote_currency"`	Price         decimal.Decimal `json:"price"`	IsStale       bool            `json:"is_stale"`	ComputedAt    string          `json:"computed_at"`}var errRateLimited = errors.New("goldprice: 429 rate limited")type cacheEntry struct {	resp    SpotResponse	expires time.Time}// Client is safe for concurrent use.type Client struct {	apiKey string	http   *http.Client	mu     sync.Mutex	cache  map[string]cacheEntry}func NewClient() *Client {	key := os.Getenv("GOLDPRICE_API_KEY")	if key == "" {		panic("GOLDPRICE_API_KEY not set")	}	return &Client{		apiKey: key,		http:   &http.Client{Timeout: timeout},		cache:  make(map[string]cacheEntry),	}}// Spot fetches the latest spot price. Cached 60s per symbol, retries 3x on 429.func (c *Client) Spot(symbol string) (SpotResponse, error) {	c.mu.Lock()	if e, ok := c.cache[symbol]; ok && time.Now().Before(e.expires) {		c.mu.Unlock()		return e.resp, nil	}	c.mu.Unlock()	var out SpotResponse	var err error	for attempt := 0; attempt < 3; attempt++ {		out, err = c.fetch(symbol)		if err == nil {			c.mu.Lock()			c.cache[symbol] = cacheEntry{resp: out, expires: time.Now().Add(ttl)}			c.mu.Unlock()			return out, nil		}		if !errors.Is(err, errRateLimited) {			return SpotResponse{}, err		}		if attempt < 2 {			time.Sleep(time.Duration(1<<attempt) * time.Second) // 1s, then 2s		}	}	return SpotResponse{}, err}func (c *Client) fetch(symbol string) (SpotResponse, error) {	req, err := http.NewRequest(http.MethodGet, apiBase+"/v1/spot/"+symbol, nil)	if err != nil {		return SpotResponse{}, err	}	req.Header.Set("X-API-Key", c.apiKey)	resp, err := c.http.Do(req)	if err != nil {		return SpotResponse{}, err	}	defer resp.Body.Close()	if resp.StatusCode == http.StatusTooManyRequests {		return SpotResponse{}, errRateLimited	}	if resp.StatusCode != http.StatusOK {		return SpotResponse{}, fmt.Errorf("goldprice: unexpected status %d", resp.StatusCode)	}	var out SpotResponse	if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {		return SpotResponse{}, err	}	return out, nil}func main() {	c := NewClient()	r, err := c.Spot("XAG-USD-SPOT")	if err != nil {		panic(err)	}	fmt.Printf("%s-%s: %s (computed %s)\n", r.Symbol, r.QuoteCurrency, r.Price.StringFixed(3), r.ComputedAt)}

Run the client

Run the file from the shell where you set GOLDPRICE_API_KEY. The program prints the silver symbol, quote currency, price to three decimals, and computation timestamp. The output below is an example, not a current quote.

Repeated calls on the same client use its cache until the entry expires. Each new process starts with an empty cache.

shell
go run goldprice.go# XAG-USD: 54.020 (computed 2026-10-23T18:12:06.354573+00:00)

Switch metal or convert silver to another currency

Use Spot() with the symbols below to fetch silver, gold, or copper. Silver and gold use US dollars per troy ounce. Copper uses US dollars per pound. Each symbol has its own cache entry.

Silver spot symbols exist only in US dollars. For silver in euros per gram, use GET /v1/convert?from=XAG&to=EUR&amount=1&unit=gram instead. Conversion supports oz, gram, and kg. See the API reference for the response format.

goldprice.go · multi-currency
c := NewClient()// Silver in USD per troy ouncec.Spot("XAG-USD-SPOT")// Gold in USD per troy ouncec.Spot("XAU-USD-SPOT")// Copper in USD per poundc.Spot("HG-USD-SPOT")

Expected output

JSON
{  "symbol": "XAG",  "quote_currency": "USD",  "unit": "troy_ounce",  "contract_type": "spot",  "price": "54.020",  "is_stale": false,  "computed_at": "2026-10-23T18:12:06.354573+00:00"}

The program prints the silver symbol, quote currency, price to three decimals, and computation timestamp. The JSON shows the response a Free key receives, with the price as a string. Read is_stale and computed_at from the actual response before using a price.

Common errors

CodeWhat you seeFix
401goldprice: unexpected status 401 on the first requestCheck that GOLDPRICE_API_KEY contains your valid API key, then restart the program. Get a key through onboarding if needed. An empty variable causes NewClient() to panic before sending a request.
429All three attempts return 429, then Spot() returns the rate limit errorReduce request volume and check your quota. Free allows 10 requests a minute and 5,000 calls a month. Reuse the client so calls share its cache. Concurrent cache misses can still send duplicate requests. Stagger calls or combine duplicate requests, and see pricing if you need more capacity.
N/APrice decoding fails or the decimal package cannot be foundPrices arrive as JSON strings. Keep Price typed as decimal.Decimal; a plain float64 field does not decode these strings with the standard JSON decoder. Run go mod tidy if the decimal import is unresolved.

Going further

  • Add a context.Context parameter to Spot() and use http.NewRequestWithContext so callers can cancel requests
  • Combine simultaneous requests for the same uncached symbol with golang.org/x/sync/singleflight
  • Handle IsStale and ComputedAt before displaying or using a silver quote
  • Use optional decimal fields if your application needs to distinguish missing bid and ask values from zero
  • See the quickstart for request setup and the API reference for response fields

Questions

Are silver prices live on every plan?

Paid plans return live silver prices. Free prices are fifteen minutes late. The client's cache can reuse a response until its entry expires. Check is_stale and computed_at when deciding whether a quote is suitable for your use.

What is the free tier quota?

Free includes silver spot, 5,000 calls a month, and 10 requests a minute, with no credit card required. Cache responses and fetch only when needed. A cache reduces repeated requests but does not guarantee that continuous polling stays within your monthly quota.

Can I get silver in euros without an API key?

Yes. Call /v1/convert?from=XAG&to=EUR&amount=1&unit=gram for silver in euros per gram. Conversion works without a key, with prices fifteen minutes late and a limit of 100 requests an hour per IP. Silver spot symbols themselves exist only in US dollars.

Why use decimal.Decimal instead of float64?

The API returns silver prices as strings, which decimal.Decimal can decode directly. Silver quotes can contain decimal places. A decimal type preserves those values for calculations without introducing binary floating point rounding when you parse the response.

Can goroutines share this client?

Yes. A sync.Mutex protects the cache map, and goroutines can share the client. However, simultaneous requests for an uncached symbol can each fetch it. Add a singleflight.Group if you need those callers to share one request and its result.

Which metals can this client fetch?

Use XAG-USD-SPOT for silver, XAU-USD-SPOT for gold, and HG-USD-SPOT for copper. Silver and gold prices are per troy ounce; copper is per pound. All these symbols quote US dollars, so keep the metal's unit with each price.

Can Claude fetch silver prices through MCP?

Yes. The goldprice.dev MCP server provides get_spot_price for silver. Connect an MCP client to https://api.goldprice.dev/v1/mcp/ without a key, or use npx @nusantara-ventures/goldprice-mcp with a key. Ask Claude for silver spot in US dollars and check the returned freshness fields.

Keep reading

Start with a free API key

5,000 requests a month on the free plan. No credit card.

Get a free key