When an integration breaks, developers paste the error into Stack Overflow. The questions that come up for a gold price API are almost always the same handful: an auth rejection, a malformed symbol, a rate-limit wall, or a CORS error from the browser. This post fixes each one directly, using goldprice.dev as the worked example. Every snippet below is a real, working call against https://api.goldprice.dev.
"401 Unauthorized" — my API key is rejected
The key goes in the Authorization header as a bearer token, and the word Bearer (with a trailing space) has to be there:
curl https://api.goldprice.dev/v1/prices?symbol=XAU-USD-SPOT \
-H "Authorization: Bearer ga_live_YOUR_KEY"
The three things that actually cause a 401: the Bearer prefix is missing, the key has a stray newline or quote from a copy-paste, or the key is being read from an env var that is not loaded. Keys are prefixed ga_live_. You do not strictly need a key to start — anonymous calls work but are capped at 100 requests per hour per IP and return a trimmed response shape, so for anything past a quick test, authenticate.
"400 invalid_symbol" — the most common first error
The symbol parameter is not a bare metal code. It is BASE-QUOTE-CONTRACT. Passing symbol=XAU returns 400 invalid_symbol; the value you want for live gold spot is XAU-USD-SPOT:
# wrong — 400 invalid_symbol
curl "https://api.goldprice.dev/v1/prices?symbol=XAU"
# right
curl "https://api.goldprice.dev/v1/prices?symbol=XAU-USD-SPOT"
Silver spot is XAG-USD-SPOT and copper is HG-USD-SPOT. Futures use the FUTURES contract, for example XAU-USD-FUTURES — note that gold futures need the Basic tier, and silver and copper need Pro, so on the free tier those symbols return 403 plan_gated. Omitting symbol entirely returns the default allowed rows for your tier, which is also a valid way to avoid the error.
"429 Too Many Requests" — handling rate limits
Every response carries an X-RateLimit-Remaining header. Read it and back off before you hit zero rather than polling blind and catching 429s. The free tier is 30 requests per minute; Basic is 120; Pro and Realtime are 500. For most apps a short client-side cache, sized to how often the price actually moves, keeps you well clear of the limit — the spot price does not change meaningfully every second, so caching for a few seconds is usually correct.
"CORS error" — calling the API from the browser
Do not call the API directly from front-end JavaScript with your key. Two reasons: it exposes ga_live_... to anyone who opens devtools, and browser CORS will block it. Proxy the call through your own backend, keep the key server-side, and have the browser hit your endpoint:
// your backend route — the key never reaches the browser
const r = await fetch(
"https://api.goldprice.dev/v1/prices?symbol=XAU-USD-SPOT",
{ headers: { Authorization: `Bearer ${process.env.GOLDPRICE_API_KEY}` } },
);
const data = await r.json();
"Which field is the price?" — reading the response
The anonymous response gives you price, bid, ask, computed_at, is_stale, a sources[] array, and a per-gram karat breakdown. Authenticate and the shape adds open_price, high_price, low_price, prev_close_price, the change fields, and divergence_bps / divergence_flag. Two fields matter for correctness:
is_stale — true when the value is older than its expected refresh window. Check it before you display a price.
sources[] — one row per upstream, each with its own price and timestamp. When sources disagree, divergence_bps tells you by how much, so you can set your own tolerance instead of trusting a single blended number.
"How do I get historical / OHLC data?"
Daily close history is GET /v1/prices/history, available on every tier — what changes is how far back you can go. The free tier returns the last 30 days, Basic the last year, and Pro the full history. So if a historical query comes back shorter than you expected, that is the window for your tier, not an auth error.
The shortest call that works
No key, correct symbol:
curl "https://api.goldprice.dev/v1/prices?symbol=XAU-USD-SPOT"
That returns a live, honest gold price with its sources and a staleness flag. When you want the full response shape and higher limits, a free key takes a minute. If you prefer a language-specific walkthrough, Fetch live gold prices in JavaScript covers the REST path end to end, and Caching gold prices and staying inside rate limits covers doing it at volume.