Blog/Developer

Build an embeddable gold-price widget

A self-contained HTML/JS snippet that fetches the live gold spot price and auto-refreshes every 60 seconds, ready to drop into any website.

Developer.md

You want to show the current gold price on a website: a portfolio page, a finance blog, a product that deals in gold-backed assets. The path of least resistance is a self-contained widget: one HTML block, no framework, no build step, drops into any page.

Here is the full thing, then the explanation.

The widget

<div id="gp-widget" style="
  display: inline-block;
  font-family: system-ui, sans-serif;
  background: #fafaf8;
  border: 1px solid #e5e3dc;
  border-radius: 8px;
  padding: 16px 24px;
  min-width: 180px;
">
  <div style="font-size: 11px; color: #888; text-transform: uppercase; letter-spacing: 0.05em;">
    Gold Spot (XAU/USD)
  </div>
  <div id="gp-price" style="font-size: 28px; font-weight: 600; color: #1a1a1a; margin: 4px 0;">
    —
  </div>
  <div id="gp-ts" style="font-size: 11px; color: #aaa;">
    Loading...
  </div>
</div>

<script>
(function () {
  var API_KEY = "ga_live_YOUR_KEY_HERE";
  var INTERVAL_MS = 60000; // 60 seconds

  function formatPrice(price) {
    return "$" + price.toLocaleString("en-US", {
      minimumFractionDigits: 2,
      maximumFractionDigits: 2,
    });
  }

  function formatTime(iso) {
    return new Date(iso).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
  }

  function fetchPrice() {
    fetch("https://api.goldprice.dev/v1/spot/XAU-USD-SPOT", {
      headers: { Authorization: "Bearer " + API_KEY },
    })
      .then(function (res) {
        if (!res.ok) throw new Error("HTTP " + res.status);
        return res.json();
      })
      .then(function (data) {
        document.getElementById("gp-price").textContent = formatPrice(data.price);
        document.getElementById("gp-ts").textContent =
          "Updated " + formatTime(data.computed_at);
      })
      .catch(function (err) {
        document.getElementById("gp-ts").textContent = "Unavailable";
        console.error("Gold price fetch failed:", err);
      });
  }

  fetchPrice();
  setInterval(fetchPrice, INTERVAL_MS);
})();
</script>

Paste it anywhere. Replace ga_live_YOUR_KEY_HERE with your key and it works.

How it works

The outer IIFE ((function () { ... })()) keeps all variables out of the global scope. No conflicts with anything else on the page.

fetch is supported in every modern browser without a polyfill. For sites that need to support very old browsers, replace fetch with XMLHttpRequest. The logic is identical, just more verbose.

The price updates every 60 seconds via setInterval. With the free tier's 30 req/min limit, 60-second polling gives you comfortable headroom even if multiple widgets are on the same page. If you embed the same key in many pages simultaneously, all those calls count against the same quota.

Choosing a refresh interval

The right interval depends on what your users need. For an informational display on a content site, 60 seconds is fine. For a product that helps users decide when to buy, you might want 15–30 seconds. The spot price does not move tick-by-tick for most retail use cases, so sub-30-second polling is usually unnecessary and burns quota faster. If you need true real-time ticks, the SSE stream endpoint is a better fit than polling.

If your page might stay open for hours, consider pausing refreshes when the tab is not visible:

function fetchIfVisible() {
  if (document.visibilityState === "visible") {
    fetchPrice();
  }
}

fetchPrice();
setInterval(fetchIfVisible, INTERVAL_MS);

This avoids draining the user's battery and your rate-limit quota when nobody is looking at the tab.

Styling for dark backgrounds

The default widget uses light colors. For a dark theme, swap the background and text values:

// In the style block, change:
background: #1a1a1a;
border-color: #333;
// And for the price text:
color: #f5f5f0;
// And the labels:
color: #666; // top label
color: #444; // timestamp

Or use CSS custom properties on the container and override them in your site's stylesheet. That way the widget adapts to dark/light mode without JavaScript.

Embedding in a CMS or no-code tool

Most CMS platforms have an HTML block or custom code widget. Paste the snippet there. If the platform strips <script> tags from inline HTML blocks (Webflow does this in rich-text areas, for example), use the platform's dedicated "embed" or "custom code" component instead; those preserve scripts.

In WordPress, use a Custom HTML block in the block editor. In Webflow, use an Embed element. In Notion-based sites, most publishing layers that support custom HTML will accept the snippet.

A note on API key exposure

This widget runs client-side, so your API key is visible in the page source. That is unavoidable for any browser-side fetch. To limit the blast radius if the key leaks, create a separate read-only key for each site where you embed the widget. If goldprice.dev's dashboard supports key scoping, use it. If a key gets abused, you rotate that one key without affecting other integrations.

Get your key via the quickstart guide.

// related guides

// goldprice.dev

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