Browser WebSocket
Подключайтесь к WebSocket API finlight.me напрямую из веб-браузера с помощью нативного WebSocket API. Это идеально для панелей, торговых терминалов и любых фронтенд-приложений, которым нужны обновления статей в реальном времени без серверного прокси.
WebSocket API браузера не поддерживает пользовательские заголовки. Аутентификация выполняется через параметры запроса вместо заголовка x-api-key. Ваш ключ API будет виден в URL WebSocket — используйте этот подход только в средах, где это приемлемо (напр., в аутентифицированных панелях). Никогда не раскрывайте ключ API в общедоступном клиентском коде.
Как это работает
- Подключитесь к
wss://wss.finlight.me(обогащённый) илиwss://wss.finlight.me/raw(raw), указав ключ API как параметр запроса - Подпишитесь, отправив JSON-сообщение с вашими критериями фильтра и
clientNonce - Получите сообщение
admit, подтверждающее ваше подключение, а затем сообщенияsendArticleпо мере поступления статей - Поддерживайте соединение, отправляя сообщения
pingкаждые 25 секунд - Отключитесь, вызвав
ws.close()
URL подключения
- Name
Обогащённый- Type
- wss://wss.finlight.me
- Description
Статьи с полным обогащением: анализ тональности, извлечение сущностей, категории и полное содержимое (в зависимости от уровня подписки).
- Name
Raw- Type
- wss://wss.finlight.me/raw
- Description
Низколатентные raw-статьи без обогащения. Лучше всего для случаев, где скорость прежде всего и нужно получать статьи как можно быстрее.
Параметры запроса
- Name
apiKey- Type
- string
- Description
Ваш ключ API finlight. Используется для аутентификации, поскольку браузерный WebSocket не может задавать пользовательские заголовки.
- Name
takeover- Type
- string
- Description
Установите
"true", чтобы автоматически заменять самое старое подключение при достижении лимита одновременных подключений.
- Name
clientVersion- Type
- string
- Description
Необязательный идентификатор вашего клиентского приложения. Напр.
"my-dashboard/1.0"
Сообщение подписки
После открытия подключения отправьте JSON-сообщение для подписки на статьи. Сервер ответит сообщением admit, а затем начнёт отправлять подходящие статьи.
- Name
clientNonce- Type
- string
- Description
Уникальный идентификатор (напр., UUID) для этого запроса подписки. Сервер возвращает его в ответе
admit.
- Name
query- Type
- string
- Description
Поисковый запрос для поиска релевантных статей. Поддерживает расширенные запросы.
- Name
sources- Type
- string[]
- Description
Фильтрация по доменам источников. Напр.
["www.reuters.com", "www.cnbc.com"]
- Name
excludeSources- Type
- string[]
- Description
Исключает определённые домены источников.
- Name
tickers- Type
- string[]
- Description
Фильтрация по тикерам. Напр.
["AAPL", "NVDA"](только обогащённый)
- Name
countries- Type
- string[]
- Description
Фильтрация по кодам стран в ISO 3166-1 alpha-2. Напр.
["US", "DE"](только обогащённый)
- Name
language- Type
- string
- Description
Фильтрация по языку (ISO 639-1). По умолчанию
en— возвращает только английский и исключает другие языки — см. Язык и охват.
Сообщения сервера
Сервер отправляет JSON-сообщения с полем action, указывающим тип сообщения:
- Name
admit- Type
- object
- Description
Отправляется после успешного рукопожатия. Содержит
leaseId,serverNow(метка времени) и вашclientNonce.
- Name
sendArticle- Type
- object
- Description
Новая статья, соответствующая вашей подписке. Данные статьи находятся в поле
data.
- Name
pong- Type
- object
- Description
Ответ на ваш «сердечный ритм»
ping.
- Name
preempted- Type
- object
- Description
Ваше подключение было заменено другой сессией (когда другой клиент подключился с
takeover: true).
- Name
error- Type
- object
- Description
Произошла ошибка. Подробности см. в поле
dataилиerror.
Полный пример
Минимальный, не зависящий от фреймворка браузерный WebSocket-клиент с автоматическим переподключением и «сердечным ритмом»:
- Аутентифицируется через параметры запроса
- Отправляет сообщение подписки при подключении
- Поддерживает интервал «сердечного ритма» 25 секунд
- Переподключается с экспоненциальной задержкой (от 500 мс до 10 с)
- Обрабатывает все типы сообщений сервера
Browser Client
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"
}
]
}
}
Обогащённый vs Raw
| Функция | Обогащённый (/) | Raw (/raw) |
|---|---|---|
| Анализ тональности | Да | Нет |
| Извлечение сущностей (компании) | Да (зависит от уровня) | Нет |
| Категории | Да | Нет |
| Полное содержимое | Да (зависит от уровня) | Нет |
| Задержка | Стандартная | Минимальная |
Фильтр: tickers | Да | Нет |
Фильтр: countries | Да | Нет |
Фильтр: query, sources, language | Да | Да |
Подписки Raw WebSocket поддерживают только фильтры query, sources, excludeSources и language. Фильтры по тикерам и странам молча игнорируются на raw-эндпоинте.
Что дальше?
- Полную модель статьи и все доступные поля см. на странице Обогащённая подписка
- Сведения, специфичные для raw, см. в Raw-подписке
- Чтобы уточнить фильтры, изучите Расширенное построение запросов
- Для поиска исторических статей наряду с потоком в реальном времени используйте REST API