Surat Gold 18k (SURA-18k) - Per Gram Convert API integration guide for Node.js
You need to price a Surat jewelry SKU called “SURA-18k” per gram in real time and expose it in a Node.js app. By the end of this guide, you’ll fetch a live XAU price from Metals-API, convert it to USD per gram, apply the 18k purity factor, and return a clean, cacheable per-gram price you can use for cart totals, RFQs, and internal ERP updates.
What we’re building: a per-gram 18k gold price from live XAU
Metals-API returns gold (XAU) quotes relative to a base currency (default USD) with troy ounce as the unit. We’ll:
- Call the Latest endpoint for XAU to get USD per troy ounce (USDXAU).
- Convert ounces to grams (31.1034768 g per troy ounce).
- Apply an 18k purity multiplier (0.75 for 18/24 karats).
- Return the SURA-18k per-gram price your app can cache and display.
Reference the official endpoints in the Documentation and confirm tradable symbols on the Symbols page. If you don’t have an API key yet, get one from Register.
Endpoints we’ll use (and why)
- Latest: fetches the current XAU quote. We’ll read both rates.XAU (oz per USD) and rates.USDXAU (USD per oz) if provided.
- Convert: optional helper to convert amounts between USD and XAU when you need to normalize order values or perform sanity checks.
We’ll stick to these two endpoints to keep the integration minimal. If you later need historical fills, OHLC, or time series for analytics and backtesting, see the Documentation.
Units, purity, and base currency
- Unit: Metals-API rates for XAU are troy ounce-based. One troy ounce = 31.1034768 grams. Do not use avoirdupois ounces.
- Purity: 18k means 18/24 parts gold = 0.75. Multiply the pure-gold per-gram price by 0.75 to price 18k items before adding spreads, making charges, and taxes.
- Base currency: Default base is USD. The Latest response can provide both rates.XAU (oz per 1 USD) and rates.USDXAU (USD per 1 oz). Use USDXAU when present to avoid manual inversion.
If you’re new to troy ounces, see the background on weight standards at the LBMA: LBMA weights and measures.
Copy-paste cURL: Latest XAU and the exact response fields you’ll need
Request:
curl -s "https://metals-api.com/api/latest?access_key=YOUR_API_KEY&symbols=XAU,USD,USDXAU"
Example JSON (real response values provided):
{"success":true,"timestamp":1790640540,"date":"2026-09-29","base":"USD","rates":{"XAU":0.00024247126715484,"USD":1,"USDXAU":4124.200000000037}}
What to use:
- rates.USDXAU: USD per troy ounce of gold. This is the cleanest way to compute per-gram prices.
- Alternative (when USDXAU is not present): invert rates.XAU. That is, 1 / rates.XAU = USD per troy ounce.
- timestamp and date: useful for cache-control and display in your UI. Times are Unix epoch seconds; treat them as UTC.
Node.js code: compute per-gram for 18k (SURA-18k)
The snippet below calls Latest, derives USD/gram for pure gold, and then multiplies by 0.75 for 18k. It guards against the case where USDXAU isn’t present by inverting rates.XAU.
import fetch from "node-fetch";
const API_KEY = process.env.METALS_API_KEY || "YOUR_API_KEY";
const TROY_OUNCE_TO_GRAM = 31.1034768;
const KARAT_18_FACTOR = 18 / 24; // 0.75
async function fetchSurat18kPerGramUSD() {
const url = `https://metals-api.com/api/latest?access_key=${API_KEY}&symbols=XAU,USD,USDXAU`;
const res = await fetch(url, { timeout: 10000 });
if (!res.ok) {
throw new Error(`Metals-API latest error: ${res.status} ${res.statusText}`);
}
const data = await res.json();
if (!data.success) {
throw new Error(`Metals-API payload error: ${JSON.stringify(data)}`);
}
const rates = data.rates || {};
let usdPerOunce;
if (typeof rates.USDXAU === "number" && isFinite(rates.USDXAU) && rates.USDXAU > 0) {
usdPerOunce = rates.USDXAU;
} else if (typeof rates.XAU === "number" && isFinite(rates.XAU) && rates.XAU > 0) {
usdPerOunce = 1 / rates.XAU; // invert oz per USD => USD per oz
} else {
throw new Error("Missing both USDXAU and invertible XAU in response");
}
const usdPerGramPure = usdPerOunce / TROY_OUNCE_TO_GRAM;
const usdPerGram18k = usdPerGramPure * KARAT_18_FACTOR;
return {
timestamp: data.timestamp, // Unix epoch seconds (UTC)
date: data.date, // YYYY-MM-DD (UTC)
usdPerOunce,
usdPerGramPure,
usdPerGram18k
};
}
// Example usage
fetchSurat18kPerGramUSD()
.then((quote) => {
// Apply your business pricing: making + spread + taxes as needed
console.log({
asOf: quote.date,
usdPerOunce: quote.usdPerOunce,
usdPerGram18k: quote.usdPerGram18k
});
})
.catch((err) => {
console.error("Pricing error:", err);
});
Notes:
- Round downstream, not in the core calculation. Keep full precision until displaying to customers.
- Cache the result to avoid hammering the API. See caching guidance below.
- If you also need pricing in INR or AED, do FX conversion downstream in your own service layer using your preferred FX rate source or Metals-API currency rates.
Optional: sanity-check using Convert
You can also cross-check the Latest price by converting a USD notional into XAU. For example, how many ounces does $1,000 buy?
Request:
curl -s "https://metals-api.com/api/convert?access_key=YOUR_API_KEY&from=USD&to=XAU&amount=1000"
Illustrative JSON response (field names and shape per API; values here are examples):
{
"success": true,
"query": { "from": "USD", "to": "XAU", "amount": 1000 },
"info": { "timestamp": 1790640587, "rate": 0.000482 },
"result": 0.482,
"unit": "troy ounces"
}
How to use it:
- info.rate is XAU per USD (oz per $1). Invert it for USD per oz if you need to compare to USDXAU.
- result is the amount of XAU for the given USD notional. Multiplying result by (31.1034768) yields grams of pure gold for that notional.
For full parameter options, see the Documentation.
Purity math and rounding for SURA-18k
- 18k multiplier: 18/24 = 0.75. Multiply the pure-gold per-gram price by 0.75 to get the metal component of 18k.
- Rounding: keep calculations at double precision and round only when presenting price-per-gram (e.g., to two decimals) or when computing extended totals (quantity × per-gram).
- Surcharges: production, wastage, shipping, tax, and hedging spreads are business rules; keep them outside the core spot-to-gram computation to avoid compounding errors.
Practical details most teams miss
1) Base currency and inversion
With base=USD (default), rates.XAU is “troy ounces per 1 USD.” The inverse, USD per ounce, is 1 / rates.XAU. Many Latest payloads also include rates.USDXAU, which is “USD per ounce” directly. Prefer USDXAU when present.
2) Timestamp and timezone
- timestamp is Unix epoch seconds in UTC.
- date is the UTC calendar date. If your storefront shows local time, convert before displaying “as of.”
3) Caching and refresh cadence
- Cache your derived per-gram price for a short TTL aligned with your plan’s update interval (e.g., 60 minutes or 10 minutes). The API’s latest update cadence varies by plan; consult the Documentation.
- A common pattern is “stale-while-revalidate”: serve the cached value instantly, then refresh in the background if the cache is older than the target interval.
4) Weekends and market closures
- Spot markets observe weekends and holidays; quotes may carry over until the next session. Use the timestamp/date from the API on your price display (e.g., “as of 2026-09-29 UTC”).
- If you need historical backfills for weekends or earlier dates, leverage the Historical or Time-series endpoints listed in the Documentation.
5) Error handling
- Guard for missing USDXAU and invert rates.XAU if available.
- Reject negative/zero or NaN values before emitting a price.
- Log the full payload once per error class for debugging; avoid logging secrets.
SURA-18k per-gram in multiple currencies
Your core calculation yields USD/gram. If you also price in regional currencies (e.g., INR for Surat retail or AED for GCC distributors), do one of the following:
- Convert USD/gram to the target currency using your FX source.
- Alternatively, set base to your target currency and fetch XAU directly relative to it (check the Symbols list for supported currencies), then compute per-gram as above.
Keep FX conversion and metal conversion steps separate in code and round only at the edge.
Small but important: karat factors and SKU mapping
Many teams embed the karat multiplier into the SKU rule set so the price engine can apply it consistently. For reference:
| Karat | Purity factor | Use |
|---|---|---|
| 24k | 1.0000 | Investment-grade bars/coins |
| 22k | 0.9167 | High-purity jewelry |
| 18k | 0.7500 | Common premium jewelry (SURA-18k) |
| 14k | 0.5833 | Value jewelry |
In this guide we apply 0.75 for SURA-18k. Keep factors as constants and unit-test them alongside your ounce-to-gram conversion.
Security, performance, and operational notes
- Do not expose your Metals-API key in client-side code. Proxy requests through your backend.
- Normalize and validate API responses; store timestamp/date alongside cached values.
- Set alerts when the per-gram price moves beyond a threshold to reprice SKUs or hedge exposures. You can build this by polling Latest and comparing to your cache.
If you’re standardizing this across multiple apps or teams, explore internal service catalogs and policy controls. Metals-API’s MCP page is a good reference point for centralized management concepts.
Full workflow recap (Node.js)
- Call Latest for XAU (and USDXAU if available).
- Derive USD per ounce (either use USDXAU or invert XAU).
- Convert to USD per gram by dividing by 31.1034768.
- Apply 0.75 to get 18k per-gram price for SURA-18k.
- Cache with a TTL aligned to your update cadence; stamp the cache with the API timestamp.
- Apply business-specific charges and taxes in a separate step before presentation.
Additional references
- Browse endpoints, parameters, and examples: Metals-API Documentation
- See supported metals and currencies: Metals-API Supported Symbols
- Background on troy weights: Troy weight (Wikipedia)
FAQ
Q: Do I have to use USD as the base?
A: No. USD is the default base in our examples. You can query with a different base if supported for your plan. If you stick with USD, use USDXAU directly when present; otherwise invert XAU.
Q: How do I get INR per gram for SURA-18k?
A: Compute USD/gram for 18k as shown, then convert to INR using your FX rate source. Alternatively, fetch XAU with base=INR (if supported), derive INR per ounce, convert to INR per gram, and apply the 0.75 factor.
Q: What should I display when markets are closed?
A: Use the latest payload’s date/timestamp and label it “as of <UTC time>.” Keep your cache valid during closures and refresh once markets reopen.
Q: How often should I refresh prices?
A: Align with your subscription’s update cadence. Cache for at least that interval and optionally add a jitter to avoid thundering-herd effects across services.
Q: Can I price by gram without calling Convert?
A: Yes. Convert is optional. Latest plus the ounce-to-gram conversion and the 18k factor are sufficient for per-gram pricing.
Ready to wire this into production? Get your API key at Register, confirm XAU in the Symbols list, and follow the Documentation to tailor parameters to your storefront or ERP.