Blog/Developer

Convert gold prices across currencies in code

How to convert a USD per troy ounce gold price into other currencies and per-gram units, with a working JavaScript example.

Developer.md

The goldprice.dev API returns gold prices in USD per troy ounce. Most developers need to display prices in local currencies, in grams rather than troy ounces, or both. The conversion arithmetic is simple; combining it cleanly with a live exchange rate is where the implementation needs care. If you're new to the weight unit conventions, Troy ounce, gram, kilo: gold weight units explained covers the conversion constants used below.

Unit conversions first

One troy ounce equals 31.1035 grams. This is a fixed constant; it does not change with market conditions.

To convert a USD/troy oz price to USD/gram:

const GRAMS_PER_TROY_OZ = 31.1035;

function perGram(pricePerTroyOz) {
  return pricePerTroyOz / GRAMS_PER_TROY_OZ;
}

If gold is at $2,387.45/oz, the per-gram price is $2,387.45 / 31.1035 = $76.75.

For per-kilogram (common in some markets):

function perKilogram(pricePerTroyOz) {
  return (pricePerTroyOz / GRAMS_PER_TROY_OZ) * 1000;
}

Currency conversion

Currency conversion requires a separate exchange rate. The goldprice.dev API returns USD prices. You supply the rate. The arithmetic is:

price_in_target = price_in_usd * usd_to_target_rate

Where usd_to_target_rate is how many units of the target currency equal one USD. If 1 USD = 15,800 IDR, then usd_to_target_rate is 15800.

function convertCurrency(usdPrice, exchangeRate) {
  return usdPrice * exchangeRate;
}

A complete example

This example fetches the gold spot price, then converts it to several currencies using a set of exchange rates you provide. In a production app, those exchange rates would come from a live currency API; here they are constants to keep the example self-contained.

const API_KEY = process.env.GOLDPRICE_API_KEY;
const GRAMS_PER_TROY_OZ = 31.1035;

async function getGoldSpot() {
  const res = await fetch("https://api.goldprice.dev/v1/spot/XAU-USD-SPOT", {
    headers: { Authorization: `Bearer ${API_KEY}` },
  });
  if (!res.ok) throw new Error(`API error ${res.status}`);
  return res.json();
}

function convertGoldPrice(usdPerOz, exchangeRates) {
  const usdPerGram = usdPerOz / GRAMS_PER_TROY_OZ;

  return Object.entries(exchangeRates).map(([currency, rate]) => ({
    currency,
    perTroyOz: parseFloat((usdPerOz * rate).toFixed(2)),
    perGram: parseFloat((usdPerGram * rate).toFixed(4)),
  }));
}

async function main() {
  const spot = await getGoldSpot();

  // Replace these with live exchange rates from a currency API
  const exchangeRates = {
    EUR: 0.921,
    GBP: 0.788,
    IDR: 15800,
    SGD: 1.341,
    JPY: 158.4,
  };

  const conversions = convertGoldPrice(spot.price, exchangeRates);

  console.log(`Gold spot: $${spot.price} USD/troy oz`);
  console.log(`As of: ${spot.timestamp}`);
  console.log("");

  for (const c of conversions) {
    console.log(`${c.currency}: ${c.perTroyOz}/oz  |  ${c.perGram}/g`);
  }
}

main().catch(console.error);

Sample output with $2,387.45 gold:

Gold spot: $2387.45 USD/troy oz
As of: 2026-06-19T08:32:11Z

EUR: 2198.82/oz  |  70.69/g
GBP: 1881.31/oz  |  60.48/g
IDR: 37721710.00/oz  |  1212630.6484/g
SGD: 3202.57/oz  |  102.97/g
JPY: 378171.48/oz  |  12161.8135/g

Formatting for display

Different locales format numbers differently. JavaScript's Intl.NumberFormat handles this cleanly:

function formatPrice(amount, currency) {
  return new Intl.NumberFormat("en-US", {
    style: "currency",
    currency,
    maximumFractionDigits: currency === "JPY" || currency === "IDR" ? 0 : 2,
  }).format(amount);
}

console.log(formatPrice(2198.82, "EUR")); // €2,198.82
console.log(formatPrice(37721710, "IDR")); // Rp37,721,710

Adjust the locale string to match your target audience. id-ID for Indonesian Rupiah, ja-JP for Japanese Yen, and so on. The currency code and locale are independent, so you can display IDR in English format or Japanese format depending on what your users expect.

Keeping exchange rates fresh

Exchange rates change throughout the trading day. For a price display that does not need to be precise to fractions of a percent, updating the exchange rates once per hour is acceptable. For applications where currency accuracy matters, such as pricing for transactions, update the exchange rates on the same cadence as the gold price.

A simple in-memory cache with a configurable TTL keeps call volume manageable:

const rateCache = { rates: null, fetchedAt: 0 };
const RATE_TTL_MS = 60 * 60 * 1000; // 1 hour

async function getExchangeRates() {
  if (rateCache.rates && Date.now() - rateCache.fetchedAt < RATE_TTL_MS) {
    return rateCache.rates;
  }
  // fetch from your currency rate provider here
  const rates = await fetchRatesFromProvider();
  rateCache.rates = rates;
  rateCache.fetchedAt = Date.now();
  return rates;
}

Summary of the arithmetic

ConversionFormula
USD/oz to USD/gprice / 31.1035
USD/oz to USD/kg(price / 31.1035) * 1000
USD/oz to TARGET/ozprice * rate
USD/oz to TARGET/g(price / 31.1035) * rate

All four conversions come down to the same two constants: 31.1035 (troy oz to grams) and the current exchange rate. Separate your data fetching from your conversion logic, and the code stays easy to test and extend. Start with the quickstart guide if you haven't fetched a price yet.

// related guides

// goldprice.dev

Live gold prices, historical OHLC, and multi-source aggregation — available via REST and SSE.