Skip to content

Quickstart (WebSocket)

Get pushed an event the instant a company’s fresh financials are ready — and the instant a filing lands — over one long-lived connection. No polling.

1. Get your API key

The stream authenticates with the same API key as the REST endpoints. Create one on the Credentials page (free plan works). Send it in the Socket.IO auth payload — that keeps it out of URLs and server logs.

2. Connect and listen

Connect to the /sec namespace and subscribe to the events you care about. The one to watch for fundamentals is data_ready.

connect · JavaScript · javascript
import { io } from 'socket.io-client';

// Connect to the SEC filings namespace. The recommended way to send your key
// is the Socket.IO auth payload — it keeps the key out of URLs and logs.
const socket = io("https://api.finradar.ai/sec", {
  auth: { api_key: "YOUR_API_KEY" },
});

socket.on('connect', () => console.log('connected'));
socket.on('connect_error', (err) => console.error('connect_error:', err.message));

// The NEW-engine event: a filing's standardized financials are now servable
// via the /api/v1/xbrl/* endpoints. Use it to invalidate a cache or trigger a
// fetch the moment fresh fundamentals land — no polling.
socket.on('data_ready', (e) => {
  console.log('fundamentals ready:', e.ticker, e.form_type, e.fiscal_period);
  // e.journal_event_id is a deduplication identity, not replay-cursor authority.
});

// A newer filing changed a previously-reported number (a real restatement).
socket.on('restatement_detected', (e) => {
  console.log('restated:', e.ticker, e.metric, e.prior_value, '->', e.new_value);
});

// The raw filings firehose (10-K, 8-K, Form 4, ...), batched.
socket.on('new_filings_batch', (rows) => {
  for (const f of rows) console.log('filed:', f.form_type, f.ticker);
});
connect · Python · python
import socketio

sio = socketio.Client()

@sio.on('data_ready', namespace='/sec')
def on_data_ready(e):
    print('fundamentals ready:', e['ticker'], e['form_type'], e['fiscal_period'])

@sio.on('new_filings_batch', namespace='/sec')
def on_filings(rows):
    for f in rows:
        print('filed:', f['form_type'], f.get('ticker'))

sio.connect(
    "https://api.finradar.ai",
    namespaces=['/sec'],
    auth={'api_key': 'YOUR_API_KEY'},
)
sio.wait()

3. The events you get

  • data_ready — a filing’s standardized financials just became servable through the As-Filed engine (/api/v1/xbrl/*). This is the fundamentals signal — fetch or refresh the moment it fires.
  • restatement_detected — a newer filing changed a number you were already serving. You get the old and new value, so you know exactly what moved.
  • new_filings_batch — newly-accepted filings (10-K, 8-K, Form 4, and so on), batched, usually within about a minute of acceptance.

4. Recover after a disconnect

Catch up after a disconnect

Replay is a disabled-by-default foundation. When it is enabled for your environment, keep a durable replay cursor separate from the journal_event_id values seen on the live stream. After a reconnect, emit replay_since with that saved cursor. The stream returns the next oldest page, up to 200 events, then a requester-only replay_complete marker. If its has_more field is true, make the next request with only the opaque continuation returned by that marker. The server owns the snapshot and binds the continuation to this socket and its original filter. Apply each successful page before persisting its next_cursor. Never advance the replay cursor from a live event. Keep a separate cursor for each cik filter.
  • Live fundamentals are buffered while this socket catches up. Outside a replay, live delivery is best effort: it is not an ordered or at-least-once feed. Deduplicate by journal_event_id.
  • Never advance the saved cursor after replay_error. A replay_gap means the cursor predates retained history and requires a full resync.
  • Operator activation is two-sided and fail closed. Keep both replay flags off during deployment. After the durable producer and retention floor are verified, set XBRL_REPLAY_ENABLED=true on the main API first and restart it while the proxy flag remains off. The proxy /health response must show xbrlReplay.backendEnabled=true and ready=false. Then set the same flag on the WebSocket proxy, restart it, and require proxyEnabled=true, backendEnabled=true, backendContract=cursor_v1, and ready=true. Roll back in reverse: disable and restart the proxy first, verify ready=false, then disable and restart the main API. Leave durable event production on so rollback does not create a future replay hole.
  • Full event contract + payload fields: Main API Stream.
  • The connection is a flat once-per-day fee; reconnecting the same day is free, so blips and deploys never multiply your cost.
  • Prefer to pull the whole corpus on a schedule instead? See Quickstart (Bulk Files).