Server-Sent Events are useful when the browser needs a one-way stream of market updates. Start by inspecting the HTTP event stream, then attach a browser client. This separates endpoint and credential failures from frontend rendering problems.
Inspect the stream with cURL
Generate a streaming token on your server using the workflow in the WebSocket guide. After checking success, take token, sseUrl and expiresAt from the response. Set STREAM_TOKEN and SSE_URL in your shell for this inspection. Do not substitute your long-lived REST API key for the token.
curl --fail-with-body --no-buffer --max-time 30 --get "$SSE_URL" \
--data-urlencode "token=$STREAM_TOKEN" \
--data-urlencode "symbols=NASDAQ:AAPL"
The command stops after thirty seconds; cURL can report its timeout exit status even after receiving valid events. That is the inspection limit, not a server-imposed stream duration. Credential-bearing command arguments can be visible to local process inspection, so use a private development environment and keep logs redacted.
The proposed neutral address is https://stream.navquote.com/sse/stream, pending live validation. Use the validated URL from token generation instead of maintaining a second independent endpoint configuration.
Read event framing before choosing a handler
SSE transports text records with data: lines. The current stream uses JSON objects whose type distinguishes connected, quote_update and other messages. For ordinary unnamed SSE records, the browser dispatches a message event; the JSON type is inside event.data.
An explicit SSE event: field is different: it selects a named browser event. Adding a listener for quote_update does not replace parsing the JSON type in an unnamed message. MDN explains both event forms. Check the actual wire format before diagnosing a silent handler.
Connect a browser and clean up
This helper accepts a validated token-generation response and an application callback. It demonstrates one connection and cleanup, not an application token endpoint. Call the returned function when leaving the screen. Your deployment must allow the browser origin through its CORS policy.
function connectQuoteEvents(connection, onQuote) {
if (!connection.success || !connection.token || !connection.sseUrl
|| !Number.isFinite(connection.expiresAt)
|| connection.expiresAt <= Date.now()) {
throw new Error("A valid streaming credential is required");
}
const url = new URL(connection.sseUrl);
url.searchParams.set("token", connection.token);
url.searchParams.set("symbols", "NASDAQ:AAPL");
const events = new EventSource(url);
events.onmessage = event => {
let message;
try { message = JSON.parse(event.data); }
catch { console.warn("Unrecognized SSE message"); return; }
if (message.type === "quote_update") onQuote(message);
if (message.type === "error") console.warn("Stream reported an error");
};
events.onerror = () => {
if (Date.now() >= connection.expiresAt) events.close();
console.warn("Stream interrupted; check connection and token expiry");
};
return () => events.close();
}
Handle expiry separately from reconnects
EventSource normally reconnects when a connection is lost. That reconnect still uses the original URL and token. Your application must monitor the supplied expiration, request renewed authorization through its server, close the old source and create a replacement when appropriate. The helper only closes an expired source when an error occurs; it does not schedule renewal.
On a temporary interruption, mark displayed quotes as potentially stale. A successful reconnect does not prove that missed updates were replayed. Fetch a new snapshot when current state is sufficient, and preserve market timestamps during reconciliation. Avoid opening one source per symbol when a single supported multi-symbol connection can serve the screen.
Keep URLs containing tokens out of analytics, error reports and shared screenshots. A short lifetime reduces credential exposure, but does not make the URL public information.