Stream SEC Filings in Real-Time Using Node.js

This guide illustrates how to use Node.js and WebSocket for streaming SEC filings in real-time, immediately after their publication on EDGAR. You'll learn to connect to the Stream API and display filing metadata - such as filing ID, CIK of the filer, form type, the acceptance date/time by the EDGAR system, and the filing URL - as soon as a filing is released.

First, install the WebSocket ws package for Node.js by running the following command in your terminal:

npm install ws

Next, copy the example code provided below into a new file on your machine, naming it websocket-client.js. Ensure to replace YOUR_API_KEY with the actual API key from your account.

Node.js
1 const WebSocket = require('ws');
2
3 const API_KEY = 'YOUR_API'; // Replace this with your actual API key
4 const STREAM_API_URL = 'wss://stream.sec-api.io?apiKey=' + API_KEY;
5
6 const ws = new WebSocket(STREAM_API_URL);
7
8 ws.on('open', () => console.log('✅ Connected to:', STREAM_API_URL));
9 ws.on('close', () => console.log('Connection closed'));
10 ws.on('error', (err) => console.log('Error:', err.message));
11
12 ws.on('message', (message) => {
13 const filings = JSON.parse(message.toString());
14 filings.forEach((filing) => {
15 console.log(
16 filing.id,
17 filing.cik,
18 filing.formType,
19 filing.filedAt,
20 filing.linkToFilingDetails,
21 );
22 });
23 });

To initiate the connection and start receiving new filings, enter the following command in your terminal:

node websocket-client.js

By following these steps, you'll set up a real-time stream of SEC filings, directly accessible from your terminal.


Advanced Example: Auto-Reconnect, Heartbeats, and Keep-Alives

This advanced Node.js code sample introduces a robust reconnection mechanism. It enables the client to monitor the connection status actively and perform an automatic reconnection if the connection drops. The strategy involves dispatching a heartbeat ping to the server at 30-second intervals, awaiting a pong response. Should the server fail to reply within a 5-second window, the client terminates the current connection and initiates a new connection process, thereby maintaining continuous connectivity.

Node.js
1 const WebSocket = require('ws');
2
3 const API_KEY = 'YOUR_API_KEY'; // Replace this with your actual API key
4 const STREAM_API_URL = 'wss://stream.sec-api.io?apiKey=' + API_KEY;
5
6 let connectionAttempts = 0;
7 const maxConnectionAttempts = 4;
8
9 function connect() {
10 const ws = new WebSocket(STREAM_API_URL);
11
12 let pingTimeout = null;
13
14 const heartbeat = () => {
15 clearTimeout(pingTimeout);
16
17 // Terminate connection if pong is not received within 5 seconds
18 pingTimeout = setTimeout(() => {
19 console.log('Pong not received, terminating connection...');
20 ws.terminate();
21 attemptReconnect();
22 }, 5000);
23
24 // Send a ping every 30 seconds
25 ws.ping();
26 };
27
28 ws.on('open', () => {
29 console.log('✅ Connected to:', STREAM_API_URL);
30 connectionAttempts = 0; // Reset connection attempts on successful connection
31 heartbeat(); // Initiate the heartbeat process
32 // Ensure heartbeat continues, so setup interval right after connection
33 setInterval(heartbeat, 30000);
34 });
35
36 ws.on('pong', () => {
37 console.log('Pong received');
38 clearTimeout(pingTimeout); // Clear the pong wait timeout
39 });
40
41 ws.on('close', () => {
42 console.log('Connection closed');
43 clearTimeout(pingTimeout);
44 attemptReconnect();
45 });
46
47 ws.on('error', (err) => {
48 console.log('Error:', err.message);
49 clearTimeout(pingTimeout);
50 attemptReconnect();
51 });
52
53 ws.on('message', (message) => {
54 const filings = JSON.parse(message.toString());
55 filings.forEach((filing) => {
56 console.log(
57 filing.id,
58 filing.cik,
59 filing.formType,
60 filing.filedAt,
61 filing.linkToFilingDetails,
62 );
63 });
64 });
65 }
66
67 function attemptReconnect() {
68 if (connectionAttempts < maxConnectionAttempts) {
69 connectionAttempts++;
70 console.log(`Attempt ${connectionAttempts} to reconnect...`);
71 setTimeout(connect, 1000 * connectionAttempts); // Exponential back-off
72 } else {
73 console.log('Max reconnection attempts reached. Giving up.');
74 }
75 }
76
77 connect();
78