BlogDeveloper

Stream live gold prices with Server-Sent Events

Connect to the goldprice.dev SSE stream to receive real-time gold price updates in the browser and Node.js, with automatic reconnect handling.

19 Jun 2026 · 3 min read

Stream live gold prices with Server-Sent Events

Polling an API every few seconds works until it doesn't. You burn request quota, you add latency equal to half your poll interval on average, and you end up with stale prices sitting in cache right when a move happens. The stream endpoint solves all three. If you're not yet at the stage where you need streaming, Caching gold prices and staying inside rate limits covers the polling approach and when to graduate to SSE.

GET /v1/prices/stream?symbol=XAU-USD-SPOT is a Server-Sent Events connection. The server pushes each new price tick as a JSON event. Your client just listens.

What you get

Each event looks like this:

JSON
{  "symbol": "XAU-USD-SPOT",  "price": "4023.66",  "computed_at": "2026-10-23T08:14:22.411Z"}

The price is a string, so convert it with Number() or parseFloat() before doing arithmetic or calling toFixed(). The computed_at timestamp tells you exactly when the price was computed server-side. You can display it directly or use it to calculate staleness.

Plus includes one SSE or WebSocket connection. Pro includes five of each, with streamed dealer prices. Free can poll /v1/spot/XAU-USD-SPOT for prices 15 minutes late.

Browser: EventSource

EventSource is built into every modern browser. You do not need a library.

html
<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <title>Gold Price</title></head><body>  <p>XAU/USD: <span id="price">—</span></p>  <p>Updated: <span id="ts">—</span></p>  <script>    // Your own server route, which adds the API key and forwards the stream    // (see "SSE: browser proxy" at /docs/stream#sse-proxy).    const source = new EventSource("/api/gold-stream");    // The stream sends unnamed events, so onmessage receives each price.    source.onmessage = (event) => {      const data = JSON.parse(event.data);      if (data.type === "state") return; // feed-state event, no price      document.getElementById("price").textContent =        `$${Number(data.price).toFixed(2)}`;      document.getElementById("ts").textContent =        new Date(data.computed_at).toLocaleTimeString();    };    source.onerror = (err) => {      console.error("SSE error", err);      // The browser reconnects automatically after a short delay.      // You can close and reopen manually if you need custom backoff.    };  </script></body></html>

The browser's EventSource API cannot set custom headers, and the API reads the key from the Authorization or X-API-Key header, so the page cannot connect to api.goldprice.dev directly.

Keep the key on your server: the server proxy example in the stream docs adds the header and forwards the events to /api/gold-stream.

Node.js: eventsource package

In Node.js you have two options: the eventsource npm package (version 2 mirrors the browser API and accepts a headers option) or a raw fetch with a streaming body. The eventsource package is simpler and reconnects automatically.

Shell
npm install eventsource@2
JavaScript
import EventSource from "eventsource";const API_KEY = process.env.GOLDPRICE_API_KEY;const url = new URL("https://api.goldprice.dev/v1/prices/stream");url.searchParams.set("symbol", "XAU-USD-SPOT");const source = new EventSource(url.toString(), {  headers: {    Authorization: `Bearer ${API_KEY}`,  },});source.onmessage = (event) => {  const data = JSON.parse(event.data);  if (data.type === "state") return; // the stream also sends feed-state events without a price  console.log(`[${data.computed_at}] XAU/USD ${data.price}`);};source.onerror = (err) => {  console.error("Stream error:", err);  // eventsource reconnects automatically after a short delay.};// Graceful shutdownprocess.on("SIGINT", () => {  source.close();  process.exit(0);});

The headers option in the Node.js eventsource package lets you send Authorization properly, unlike the browser version. Keep your key in an environment variable, not in source.

Reconnect behavior

SSE reconnects automatically when the connection drops. The browser waits briefly before reconnecting. If you need tighter control (say, you want to give up after five consecutive failures and alert an operator), track the error count yourself:

JavaScript
let errorCount = 0;const MAX_ERRORS = 5;source.onerror = (err) => {  errorCount++;  if (errorCount >= MAX_ERRORS) {    console.error("Too many stream errors, stopping.");    source.close();    // Alert, then decide whether to restart with a longer delay.    return;  }  console.warn(`Stream error #${errorCount}, will retry automatically`);};source.onmessage = () => {  // Reset on successful message  errorCount = 0;};

When to use polling instead

The stream is the right choice when you need sub-second latency and you're keeping a persistent connection open: a dashboard, a price ticker, a trading interface. For background jobs that check the price once every few minutes, the plain GET /v1/spot/XAU-USD-SPOT endpoint is simpler and cheaper on your rate-limit quota.

The free tier covers 5,000 calls per month at up to 10 requests per minute. If your polling frequency fits inside that budget, start there and graduate to the stream when you need it. See pricing to compare plan limits.

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

Keep reading

Start with a free API key

5,000 requests a month on the free plan. No credit card.

Get a free key