sifting/io
Commodities
6 min readSiftingIO Team

XAUUSD explained: gold price per gram, karat, and local currency math with a market data API

What XAUUSD means, the exact troy ounce to gram conversion, 24k/22k/18k karat math, and lira pricing via USDTRY, computed from real SiftingIO market data.

XAUUSD explained: gold price per gram, karat, and local currency math with a market data API

XAUUSD is how global markets quote the spot gold price: XAU, the ISO 4217 code for one troy ounce of gold, priced in US dollars and read exactly like a currency pair. A quote of 4042.68 means one troy ounce costs 4,042.68 dollars. That number alone answers almost no practical question. Jewelry buyers think in grams and karats, treasuries think in kilograms, and a reader in Istanbul thinks in lira per gram. This post explains the convention behind the symbol, works the conversion math with real numbers, and ends with code that turns a live XAUUSD feed into a gram, karat, and local-currency table.

Every worked figure below comes from SiftingIO data for July 31, 2026: an XAUUSD daily close of 4042.68 and a USDTRY close of 47.5356. Nothing is typed in from memory, so the arithmetic can be checked end to end.

Why gold trades as XAUUSD#

ISO 4217, the standard behind codes like USD, JPY, and TRY, reserves a block of codes for precious metals: XAU for gold, XAG for silver, XPT for platinum. The leading X marks a unit no single country issues, and AU is gold's chemical symbol, from the Latin aurum. The unit of account attached to XAU is one troy ounce.

Because gold has a currency code, the spot market quotes it like forex. XAU is the base, USD is the quote, and the price is dollars per troy ounce for immediate settlement, the spot convention, rather than a futures date. There's no single official venue printing "the" gold price. Spot gold trades continuously across sessions and dealers worldwide, so every published number is one venue's view of the market. SiftingIO treats that the way it treats forex: the XAUUSD symbol on the commodities feed carries a single aggregated price built from multiple independent venues, with bid, ask, and last trade.

If the task is just pulling that number over REST, the gold and oil spot price post already covers those endpoints in detail. What follows is about the unit math on top of the number.

Troy ounce to gram, the conversion people actually search for#

One troy ounce is exactly 31.1034768 grams. Divide the XAUUSD price by that constant to get dollars per gram; multiply the per-gram figure by 1,000 for a kilogram.

With the July 31 close of 4042.68:

price per gram      = 4042.68 / 31.1034768 = 129.98 USD
price per kilogram  = 129.9752 * 1000      = 129,975.18 USD

Purity is the next step. XAUUSD prices pure metal, which jewelry markets call 24 karat. A karat is one twenty-fourth of gold content by mass, so the metal value of an alloy scales by karat divided by 24:

22k: 129.9752 * 22/24 = 119.14 USD per gram
18k: 129.9752 * 18/24 =  97.48 USD per gram
14k: 129.9752 * 14/24 =  75.82 USD per gram

These are metal-content reference values. A retail jewelry price sits above them (fabrication and margin), a scrap buyer quotes below them, and the spot-derived figure is the anchor both sides negotiate around.

Gold in lira: crossing XAUUSD with USDTRY#

Some of the world's most active physical gold markets don't think in dollars at all. Turkey is the clearest example, where the everyday retail unit is lira per gram. Producing that number takes one more pair from the forex feed: USDTRY, the number of lira one dollar buys.

The cross-rate logic works by chasing units until the dollar cancels:

XAUUSD        = 4042.68  dollars per troy ounce
USDTRY        = 47.5356  lira per dollar
TRY per ounce = 4042.68 * 47.5356       = 192,171.22
TRY per gram  = 192,171.22 / 31.1034768 = 6,178.45
22k TRY/gram  = 6,178.45 * 22/24        = 5,663.58

Multiplying is correct here because USD is the quote currency of XAUUSD and the base of USDTRY, so the dollar appears once on each side and drops out. If the local pair were quoted the other way around, dollars per unit of local currency, the rate would divide instead. Writing the units out catches that mistake before production traffic does. The same two-pair pattern yields gold in yen (USDJPY), rand (USDZAR), or pesos (USDMXN) with no new code, since commodities and forex stream from the same API key.

One caution: both inputs move. A lira-per-gram figure built from a fresh gold tick and a minutes-old currency rate drifts visibly on a volatile day, so fetch both symbols at the same moment, either as paired REST snapshots or over one WebSocket connection. These conversions are bookkeeping; no figure in this post is a forecast or a recommendation.

Fetching XAUUSD over REST and WebSocket#

Two REST calls cover the snapshot and the history:

# Live snapshot: latest aggregated gold trade
curl -H "X-API-Key: $SIFTING_KEY" \
  "https://api.sifting.io/v1/last/trade/commodities/XAUUSD"

# Last 30 daily bars (bar endpoints require gzip)
curl --compressed -H "X-API-Key: $SIFTING_KEY" \
  "https://api.sifting.io/v1/hist/commodities/XAUUSD/bars?interval=1d&limit=30"

The conversion table itself is pure arithmetic and worth keeping as one small function:

TROY_OUNCE_GRAMS = 31.1034768

def gold_table(usd_per_oz, usd_local=1.0, karats=(24, 22, 18, 14)):
    """Per-gram price by karat; usd_local applies a USD-base forex rate like USDTRY."""
    per_gram = usd_per_oz / TROY_OUNCE_GRAMS
    return {k: round(per_gram * k / 24 * usd_local, 2) for k in karats}

print(gold_table(4042.68))           # {24: 129.98, 22: 119.14, 18: 97.48, 14: 75.82}
print(gold_table(4042.68, 47.5356))  # {24: 6178.45, 22: 5663.58, 18: 4633.84, 14: 3604.09}

For a live table, stream both symbols on one WebSocket connection. Commodities stream under the product name com, forex under fx:

import WebSocket from "ws";

const ws = new WebSocket(`wss://stream.sifting.io/ws/v1?key=${process.env.SIFTING_KEY}`);
const last = { XAUUSD: null, USDTRY: null };

ws.on("open", () => {
  ws.send(JSON.stringify({ op: "subscribe", product: "com", symbols: ["XAUUSD"] }));
  ws.send(JSON.stringify({ op: "subscribe", product: "fx", symbols: ["USDTRY"] }));
  setInterval(() => ws.send(JSON.stringify({ op: "ping" })), 60_000);
});

ws.on("message", (raw) => {
  const msg = JSON.parse(raw);
  if (msg.f !== "tick") return;
  last[msg.s] = msg.p;
  if (last.XAUUSD && last.USDTRY) {
    const gram = last.XAUUSD / 31.1034768;
    console.log(`24k ${gram.toFixed(2)} USD/g | ${(gram * last.USDTRY).toFixed(2)} TRY/g`);
  }
});

On subscribe the server replays the last cached tick immediately, so the table fills before the first live update arrives. Reconnect handling, backoff, and the full tick format are covered in the WebSocket docs; the snippet above stays deliberately minimal.

Common pitfalls#

  • The wrong ounce. A troy ounce is 31.1034768 grams; the avoirdupois ounce on a kitchen scale is 28.3495 grams. Dividing by the wrong one inflates every per-gram figure by about 9.7 percent, an error small enough to pass review and large enough to matter on a kilogram order.
  • Uncompressed history requests get rejected. The bars endpoint requires gzip, so send Accept-Encoding: gzip. curl needs the, compressed flag, while Python requests and the official SDKs negotiate it automatically.
  • Guessing the WebSocket product name. Subscribing with product "commodities" returns {"f":"error","code":"unknown_product"}; the correct string is com. And keep the connection alive: send a ping at least every 60 seconds, because the server closes any connection that stays silent for 90.

Both feeds used here have a free tier with 10,000 REST calls a month and five WebSocket symbol subscriptions, no card required. Start building free

Keep reading

Related posts