Browser WebSocket

Connect to the Finlight.me WebSocket API directly from a web browser using the native WebSocket API. This is ideal for dashboards, trading terminals, and any frontend application that needs real-time article updates without a server-side proxy.


How It Works

  1. Connect to wss://wss.finlight.me (enhanced) or wss://wss.finlight.me/raw (raw) with your API key as a query parameter
  2. Subscribe by sending a JSON message with your filter criteria and a clientNonce
  3. Receive an admit message confirming your connection, followed by sendArticle messages as articles arrive
  4. Keep alive by sending ping messages every 25 seconds
  5. Disconnect by calling ws.close()

Connection URLs

  • Name
    Enhanced
    Type
    wss://wss.finlight.me
    Description

    Articles with full enrichment: sentiment analysis, entity extraction, categories, and full content (based on your subscription tier).

  • Name
    Raw
    Type
    wss://wss.finlight.me/raw
    Description

    Low-latency raw articles without enrichment. Best for speed-first use cases where you need articles as fast as possible.

Query Parameters

  • Name
    apiKey
    Type
    string
    Description

    Your Finlight API key. Used for authentication since browser WebSocket cannot set custom headers.

  • Name
    takeover
    Type
    string
    Description

    Set to "true" to automatically replace the oldest connection when your concurrent connection limit is reached.

  • Name
    clientVersion
    Type
    string
    Description

    Optional identifier for your client application. E.g. "my-dashboard/1.0"


Subscribe Message

After the connection opens, send a JSON message to subscribe to articles. The server will respond with an admit message, then begin pushing matching articles.

  • Name
    clientNonce
    Type
    string
    Description

    A unique identifier (e.g., UUID) for this subscription request. The server echoes it back in the admit response.

  • Name
    query
    Type
    string
    Description

    Search query to find relevant articles. Supports advanced queries.

  • Name
    sources
    Type
    string[]
    Description

    Filter by source domains. E.g. ["www.reuters.com", "www.cnbc.com"]

  • Name
    excludeSources
    Type
    string[]
    Description

    Exclude specific source domains.

  • Name
    tickers
    Type
    string[]
    Description

    Filter by stock ticker symbols. E.g. ["AAPL", "NVDA"] (Enhanced only)

  • Name
    countries
    Type
    string[]
    Description

    Filter by country codes in ISO 3166-1 alpha-2. E.g. ["US", "DE"] (Enhanced only)

  • Name
    language
    Type
    string
    Description

    Filter by language in ISO 639-1. E.g. "en"


Server Messages

The server sends JSON messages with an action field indicating the message type:

  • Name
    admit
    Type
    object
    Description

    Sent after a successful handshake. Contains leaseId, serverNow (timestamp), and your clientNonce.

  • Name
    sendArticle
    Type
    object
    Description

    A new article matching your subscription. The article data is in the data field.

  • Name
    pong
    Type
    object
    Description

    Response to your ping heartbeat.

  • Name
    preempted
    Type
    object
    Description

    Your connection was replaced by another session (when another client connected with takeover: true).

  • Name
    error
    Type
    object
    Description

    An error occurred. Check the data or error field for details.


WEBSOCKETBrowser

Complete Example

A minimal, framework-agnostic browser WebSocket client with automatic reconnection and heartbeat:

  • Authenticates via query parameters
  • Sends a subscribe message on connect
  • Maintains a 25-second heartbeat interval
  • Reconnects with exponential backoff (500ms to 10s)
  • Handles all server message types

Browser Client

WEBSOCKET
wss://wss.finlight.me
const API_KEY = 'YOUR_API_KEY'
const WSS_URL = 'wss://wss.finlight.me' // Use '/raw' path for raw articles

let ws = null
let pingInterval = null
let reconnectTimeout = null
let reconnectAttempt = 0

function connect(filters = {}) {
  const params = new URLSearchParams({
    apiKey: API_KEY,
    takeover: 'true',
    clientVersion: 'my-app/1.0',
  })
  const url = `${WSS_URL}?${params}`

  ws = new WebSocket(url)

  ws.onopen = () => {
    console.log('Connected')
    reconnectAttempt = 0

    // Send subscription with filters
    ws.send(JSON.stringify({
      clientNonce: crypto.randomUUID(),
      query: filters.query || '',
      language: filters.language || 'en',
      sources: filters.sources || [],
      tickers: filters.tickers || [],
      countries: filters.countries || [],
    }))

    // Start heartbeat
    pingInterval = setInterval(() => {
      if (ws.readyState === WebSocket.OPEN) {
        ws.send(JSON.stringify({
          action: 'ping',
          t: Date.now(),
        }))
      }
    }, 25000)
  }

  ws.onmessage = (event) => {
    const msg = JSON.parse(event.data)

    switch (msg.action) {
      case 'admit':
        console.log('Admitted, lease:', msg.leaseId)
        break
      case 'sendArticle':
        console.log('Article:', msg.data.title)
        // Handle the article here
        break
      case 'pong':
        break // Heartbeat OK
      case 'preempted':
        console.warn('Connection replaced')
        break
      case 'error':
        console.error('Server error:', msg.data || msg.error)
        break
    }
  }

  ws.onclose = (event) => {
    clearInterval(pingInterval)

    // Don't reconnect on policy violations
    if (event.code === 1008 || event.code === 4002) {
      console.error('Connection blocked')
      return
    }

    // Exponential backoff reconnect
    const delay = Math.min(500 * 2 ** reconnectAttempt, 10000)
    reconnectAttempt++
    console.log(`Reconnecting in ${delay}ms...`)
    reconnectTimeout = setTimeout(() => connect(filters), delay)
  }

  ws.onerror = () => console.error('WebSocket error')
}

function disconnect() {
  clearInterval(pingInterval)
  clearTimeout(reconnectTimeout)
  if (ws) ws.close(1000)
}

// Usage
connect({ query: 'Nvidia', language: 'en', countries: ['US'] })

admit Response

{
  "action": "admit",
  "leaseId": "a1b2c3d4-e5f6-4789-abcd-ef0123456789",
  "serverNow": 1708185600000,
  "clientNonce": "your-uuid-here"
}

sendArticle Response

{
  "action": "sendArticle",
  "data": {
    "link": "https://www.reuters.com/technology/nvidia-2026-02-17",
    "source": "www.reuters.com",
    "title": "Nvidia Reports Record Revenue",
    "summary": "Nvidia announced record quarterly revenue...",
    "publishDate": "2026-02-17T10:30:00Z",
    "language": "en",
    "sentiment": "positive",
    "confidence": 0.92,
    "countries": ["US"],
    "categories": ["markets", "technology"],
    "companies": [
      {
        "name": "NVIDIA Corporation",
        "ticker": "NVDA",
        "country": "US"
      }
    ]
  }
}

Enhanced vs Raw

FeatureEnhanced (/)Raw (/raw)
Sentiment analysisYesNo
Entity extraction (companies)Yes (tier-dependent)No
CategoriesYesNo
Full contentYes (tier-dependent)No
LatencyStandardLowest
Filter: tickersYesNo
Filter: countriesYesNo
Filter: query, sources, languageYesYes

What's Next?