Browser WebSocket
네이티브 WebSocket API를 사용하여 웹 브라우저에서 직접 finlight.me WebSocket API에 연결하세요. 이는 대시보드, 트레이딩 터미널, 그리고 서버 측 프록시 없이 실시간 기사 업데이트가 필요한 모든 프런트엔드 애플리케이션에 이상적입니다.
브라우저 WebSocket API는 사용자 지정 헤더를 지원하지 않습니다. 인증은 x-api-key 헤더 대신 쿼리 매개변수로 처리됩니다. API 키가 WebSocket URL에 노출되므로, 이 방식은 그것이 허용되는 환경(예: 인증된 대시보드)에서만 사용하세요. 공개적으로 접근 가능한 클라이언트 측 코드에 API 키를 절대 노출하지 마세요.
작동 방식
- API 키를 쿼리 매개변수로 하여
wss://wss.finlight.me(보강) 또는wss://wss.finlight.me/raw(raw)에 연결합니다 - 필터 기준과
clientNonce가 포함된 JSON 메시지를 보내 구독합니다 - 연결을 확인하는
admit메시지를 받고, 이어서 기사가 도착하는 대로sendArticle메시지를 받습니다 - 25초마다
ping메시지를 보내 연결을 유지합니다 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
finlight API 키. 브라우저 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"](보강 전용)
서버 메시지
서버는 메시지 유형을 나타내는 action 필드가 있는 JSON 메시지를 보냅니다:
- 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초 하트비트 간격을 유지합니다
- 지수 백오프(500ms~10s)로 재연결합니다
- 모든 서버 메시지 유형을 처리합니다
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 엔드포인트에서 조용히 무시됩니다.