Documentation
¶
Overview ¶
Package finlight is the official Go client for the finlight.me API.
It provides access to the REST API (article search, article lookup by link, source listing), real-time article streaming over WebSocket, and webhook signature verification.
Quick start ¶
client, err := finlight.New(finlight.Config{APIKey: os.Getenv("FINLIGHT_API_KEY")})
if err != nil {
log.Fatal(err)
}
resp, err := client.Articles.FetchArticles(ctx, finlight.GetArticlesParams{Query: "nvidia"})
Streaming ¶
WebSocket streaming uses a range-over-func iterator. The internal reconnect loop (exponential backoff, proactive connection rotation, rate-limit handling) is transparent to the consumer:
for article := range client.Websocket.Stream(ctx, finlight.GetArticlesWebSocketParams{}) {
fmt.Println(article.Title)
}
if err := client.Websocket.Err(); err != nil {
log.Fatal(err)
}
Stop streaming by breaking out of the loop or cancelling the context.
Index ¶
- Variables
- type APIError
- type Article
- type ArticleResponse
- type ArticleService
- type Category
- type Client
- type Company
- type Config
- type FlexFloat
- type FlexTime
- type GetArticleByLinkParams
- type GetArticlesParams
- type GetArticlesWebSocketParams
- type GetRawArticlesWebSocketParams
- type Listing
- type OrderBy
- type RawArticle
- type RawWebSocketClient
- type SortOrder
- type Source
- type SourceService
- type WebSocketClient
- type WebSocketOptions
- type WebhookVerificationError
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrBlocked = errors.New("finlight: connection rejected by server (blocked)")
ErrBlocked is returned by the WebSocket clients' Err method when the server permanently rejected the connection (close code 1008). Reconnecting will not help; contact finlight support.
Functions ¶
This section is empty.
Types ¶
type Article ¶
type Article struct {
Link string `json:"link"`
Title string `json:"title"`
PublishDate FlexTime `json:"publishDate"`
Source string `json:"source"`
Language string `json:"language"`
Summary string `json:"summary,omitempty"`
Images []string `json:"images,omitempty"`
CreatedAt *FlexTime `json:"createdAt,omitempty"`
RevisedDate *FlexTime `json:"revisedDate,omitempty"`
IsUpdate *bool `json:"isUpdate,omitempty"`
Categories []string `json:"categories,omitempty"`
Sentiment string `json:"sentiment,omitempty"`
Confidence *FlexFloat `json:"confidence,omitempty"`
Content string `json:"content,omitempty"`
Companies []Company `json:"companies,omitempty"`
Countries []string `json:"countries,omitempty"`
}
Article is an enriched news article as returned by the REST API, the enhanced WebSocket stream, and webhooks.
func ConstructWebhookEvent ¶
func ConstructWebhookEvent(rawBody []byte, signature, endpointSecret, timestamp string) (*Article, error)
ConstructWebhookEvent verifies a finlight webhook and returns the contained article.
rawBody must be the unmodified request body. signature is the value of the X-Webhook-Signature header (with or without the "sha256=" prefix). endpointSecret is your webhook secret from the finlight dashboard. timestamp is the X-Webhook-Timestamp header; pass "" if the webhook has none, otherwise it is included in the signed message and checked against a 5-minute replay tolerance.
Example ¶
package main
import (
"fmt"
"io"
"net/http"
"os"
finlight "github.com/callbk/finlight-client-go"
)
func main() {
http.HandleFunc("/webhook", func(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
article, err := finlight.ConstructWebhookEvent(
body,
r.Header.Get("X-Webhook-Signature"),
os.Getenv("FINLIGHT_WEBHOOK_SECRET"),
r.Header.Get("X-Webhook-Timestamp"),
)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
fmt.Println("new article:", article.Title)
w.WriteHeader(http.StatusOK)
})
}
Output:
type ArticleResponse ¶
type ArticleResponse struct {
Status string `json:"status"`
Page int `json:"page"`
PageSize int `json:"pageSize"`
Articles []Article `json:"articles"`
}
ArticleResponse is the paginated result of ArticleService.FetchArticles.
type ArticleService ¶
type ArticleService struct {
// contains filtered or unexported fields
}
ArticleService fetches financial news articles.
func (*ArticleService) FetchArticleByLink ¶
func (s *ArticleService) FetchArticleByLink(ctx context.Context, params GetArticleByLinkParams) (*Article, error)
FetchArticleByLink fetches a single article by its URL.
func (*ArticleService) FetchArticles ¶
func (s *ArticleService) FetchArticles(ctx context.Context, params GetArticlesParams) (*ArticleResponse, error)
FetchArticles searches articles matching params and returns one result page.
type Category ¶
type Category string
Category is an article category assigned by finlight's classification.
const ( CategoryMarkets Category = "markets" CategoryEconomy Category = "economy" CategoryBusiness Category = "business" CategoryPolitics Category = "politics" CategoryGeopolitics Category = "geopolitics" CategoryRegulation Category = "regulation" CategoryTechnology Category = "technology" CategoryEnergy Category = "energy" CategoryCommodities Category = "commodities" CategoryCrypto Category = "crypto" CategoryHealth Category = "health" CategoryClimate Category = "climate" CategorySecurity Category = "security" )
All categories known to the API.
type Client ¶
type Client struct {
Articles *ArticleService
Sources *SourceService
Websocket *WebSocketClient // enhanced article stream, default options
RawWebsocket *RawWebSocketClient // raw article stream, default options
}
Client is the entry point to the finlight API.
func New ¶
New validates cfg, applies defaults, and returns a ready-to-use client. For WebSocket clients with custom options use NewWebSocketClient or NewRawWebSocketClient.
Example ¶
package main
import (
"context"
"fmt"
"log"
"os"
finlight "github.com/callbk/finlight-client-go"
)
func main() {
client, err := finlight.New(finlight.Config{APIKey: os.Getenv("FINLIGHT_API_KEY")})
if err != nil {
log.Fatal(err)
}
resp, err := client.Articles.FetchArticles(context.Background(), finlight.GetArticlesParams{
Query: "nvidia",
PageSize: 10,
Categories: []finlight.Category{finlight.CategoryTechnology},
})
if err != nil {
log.Fatal(err)
}
for _, article := range resp.Articles {
fmt.Println(article.Title)
}
}
Output:
type Company ¶
type Company struct {
CompanyID int `json:"companyId"`
Confidence *FlexFloat `json:"confidence,omitempty"`
Country string `json:"country,omitempty"`
Exchange string `json:"exchange,omitempty"`
Industry string `json:"industry,omitempty"`
Sector string `json:"sector,omitempty"`
Name string `json:"name"`
Ticker string `json:"ticker"`
ISIN string `json:"isin,omitempty"`
OpenFIGI string `json:"openfigi,omitempty"`
PrimaryListing *Listing `json:"primaryListing,omitempty"`
ISINs []string `json:"isins,omitempty"`
OtherListings []Listing `json:"otherListings,omitempty"`
}
Company is an entity recognized in an article.
type Config ¶
type Config struct {
APIKey string // required
BaseURL string // default "https://api.finlight.me"
WssURL string // default "wss://wss.finlight.me"
Timeout time.Duration // per-request timeout, default 5s
RetryCount int // total request attempts, default 3
Logger *slog.Logger // default: silent
HTTPClient *http.Client // optional override; default uses Timeout
}
Config configures the finlight client. Only APIKey is required; zero-valued fields fall back to the documented defaults.
type FlexFloat ¶
type FlexFloat float64
FlexFloat is a float64 that also unmarshals from JSON strings ("0.95"). The API and webhooks deliver confidence values in both representations.
func (FlexFloat) MarshalJSON ¶
func (*FlexFloat) UnmarshalJSON ¶
type FlexTime ¶
FlexTime is a time.Time that unmarshals from the timestamp formats used by the finlight API (RFC 3339 with or without zone, space-separated, date-only).
func (FlexTime) MarshalJSON ¶
func (*FlexTime) UnmarshalJSON ¶
type GetArticleByLinkParams ¶
type GetArticleByLinkParams struct {
Link string // required
IncludeContent bool
IncludeEntities bool
}
GetArticleByLinkParams are the parameters for ArticleService.FetchArticleByLink.
type GetArticlesParams ¶
type GetArticlesParams struct {
// Query supports the finlight query language, e.g.
// `(ticker:AAPL OR ticker:NVDA) AND NOT source:www.reuters.com AND "Elon Musk"`.
Query string `json:"query,omitempty"`
// Deprecated: use Sources.
Source string `json:"source,omitempty"`
Sources []string `json:"sources,omitempty"`
ExcludeSources []string `json:"excludeSources,omitempty"`
OptInSources []string `json:"optInSources,omitempty"`
From string `json:"from,omitempty"` // YYYY-MM-DD or ISO 8601
To string `json:"to,omitempty"`
Language string `json:"language,omitempty"`
Tickers []string `json:"tickers,omitempty"`
IncludeEntities bool `json:"includeEntities,omitempty"`
ExcludeEmptyContent bool `json:"excludeEmptyContent,omitempty"`
IncludeContent bool `json:"includeContent,omitempty"`
OrderBy OrderBy `json:"orderBy,omitempty"`
Order SortOrder `json:"order,omitempty"`
PageSize int `json:"pageSize,omitempty"` // 1–1000
Page int `json:"page,omitempty"`
Countries []string `json:"countries,omitempty"`
Categories []Category `json:"categories,omitempty"`
}
GetArticlesParams are the search parameters for ArticleService.FetchArticles. Zero-valued fields are omitted from the request; the server applies its documented defaults.
type GetArticlesWebSocketParams ¶
type GetArticlesWebSocketParams struct {
Query string `json:"query,omitempty"`
Sources []string `json:"sources,omitempty"`
ExcludeSources []string `json:"excludeSources,omitempty"`
OptInSources []string `json:"optInSources,omitempty"`
Language string `json:"language,omitempty"`
// Deprecated: use IncludeContent.
Extended bool `json:"extended,omitempty"`
Tickers []string `json:"tickers,omitempty"`
IncludeEntities bool `json:"includeEntities,omitempty"`
ExcludeEmptyContent bool `json:"excludeEmptyContent,omitempty"`
IncludeContent bool `json:"includeContent,omitempty"`
Countries []string `json:"countries,omitempty"`
Categories []Category `json:"categories,omitempty"`
IncludeUpdates bool `json:"includeUpdates,omitempty"`
}
GetArticlesWebSocketParams filter the enhanced WebSocket stream.
type GetRawArticlesWebSocketParams ¶
type GetRawArticlesWebSocketParams struct {
Query string `json:"query,omitempty"`
Sources []string `json:"sources,omitempty"`
ExcludeSources []string `json:"excludeSources,omitempty"`
OptInSources []string `json:"optInSources,omitempty"`
Language string `json:"language,omitempty"`
IncludeUpdates bool `json:"includeUpdates,omitempty"`
}
GetRawArticlesWebSocketParams filter the raw WebSocket stream.
type Listing ¶
type Listing struct {
Ticker string `json:"ticker"`
ExchangeCode string `json:"exchangeCode"`
ExchangeCountry string `json:"exchangeCountry"`
}
Listing describes one exchange listing of a company.
type RawArticle ¶
type RawArticle struct {
Link string `json:"link"`
Title string `json:"title"`
PublishDate FlexTime `json:"publishDate"`
Source string `json:"source"`
Language string `json:"language"`
Summary string `json:"summary,omitempty"`
Images []string `json:"images,omitempty"`
CreatedAt *FlexTime `json:"createdAt,omitempty"`
RevisedDate *FlexTime `json:"revisedDate,omitempty"`
IsUpdate *bool `json:"isUpdate,omitempty"`
Categories []string `json:"categories,omitempty"`
}
RawArticle is an unenriched article as delivered by the raw WebSocket stream (no sentiment, entities, or content).
type RawWebSocketClient ¶
type RawWebSocketClient struct {
// contains filtered or unexported fields
}
RawWebSocketClient streams unenriched articles in real time (no sentiment, entities, or content — lower latency). No duplicate suppression.
A client instance supports one active stream at a time.
func NewRawWebSocketClient ¶
func NewRawWebSocketClient(cfg Config, opts WebSocketOptions) *RawWebSocketClient
NewRawWebSocketClient returns a raw streaming client with custom options. Client instances created by New use default options.
func (*RawWebSocketClient) Err ¶
func (w *RawWebSocketClient) Err() error
Err reports why the last stream ended: nil after a normal shutdown, or a terminal error such as ErrBlocked.
func (*RawWebSocketClient) Stream ¶
func (w *RawWebSocketClient) Stream(ctx context.Context, params GetRawArticlesWebSocketParams) iter.Seq[RawArticle]
Stream connects to the raw finlight WebSocket and yields articles matching params. See WebSocketClient.Stream for the streaming semantics.
type Source ¶
type Source struct {
Domain string `json:"domain"`
IsContentAvailable bool `json:"isContentAvailable"`
IsDefaultSource bool `json:"isDefaultSource"`
}
Source is a news source available through the API.
type SourceService ¶
type SourceService struct {
// contains filtered or unexported fields
}
SourceService lists the news sources available through the API.
func (*SourceService) GetSources ¶
func (s *SourceService) GetSources(ctx context.Context) ([]Source, error)
GetSources returns all sources with their availability flags.
type WebSocketClient ¶
type WebSocketClient struct {
// contains filtered or unexported fields
}
WebSocketClient streams enriched articles in real time. Duplicate articles (same link within the last 10 deliveries) are suppressed.
A client instance supports one active stream at a time.
func NewWebSocketClient ¶
func NewWebSocketClient(cfg Config, opts WebSocketOptions) *WebSocketClient
NewWebSocketClient returns a streaming client with custom options. Client instances created by New use default options.
func (*WebSocketClient) Err ¶
func (w *WebSocketClient) Err() error
Err reports why the last stream ended: nil after a normal shutdown (context cancelled or consumer break), or a terminal error such as ErrBlocked.
func (*WebSocketClient) Stream ¶
func (w *WebSocketClient) Stream(ctx context.Context, params GetArticlesWebSocketParams) iter.Seq[Article]
Stream connects to the finlight WebSocket and yields articles matching params. Reconnects (exponential backoff, proactive rotation, rate-limit waits) are handled internally. End the stream by breaking out of the range loop or cancelling ctx; afterwards check Err for a terminal failure.
Example ¶
package main
import (
"context"
"fmt"
"log"
"os"
"os/signal"
finlight "github.com/callbk/finlight-client-go"
)
func main() {
client, err := finlight.New(finlight.Config{APIKey: os.Getenv("FINLIGHT_API_KEY")})
if err != nil {
log.Fatal(err)
}
// Stop streaming on Ctrl+C; breaking out of the loop works too.
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
for article := range client.Websocket.Stream(ctx, finlight.GetArticlesWebSocketParams{
Tickers: []string{"AAPL", "NVDA"},
}) {
fmt.Println(article.Title)
}
if err := client.Websocket.Err(); err != nil {
log.Fatal(err)
}
}
Output:
type WebSocketOptions ¶
type WebSocketOptions struct {
PingInterval time.Duration // application-level ping cadence, default 25s
PongTimeout time.Duration // force reconnect when no pong arrives, default 60s
BaseReconnectDelay time.Duration // first reconnect backoff, default 500ms
MaxReconnectDelay time.Duration // backoff cap, default 10s
ConnectionLifetime time.Duration // proactive rotation, default 115min (under the 2h server cap)
Takeover bool // take over an existing connection for the same key
OnClose func(code int, reason string)
}
WebSocketOptions tune the streaming clients. Zero values fall back to the documented defaults, which match the TypeScript and Python clients.
type WebhookVerificationError ¶
type WebhookVerificationError struct {
// contains filtered or unexported fields
}
WebhookVerificationError is returned by ConstructWebhookEvent when a webhook fails signature, timestamp, or payload validation.
func (*WebhookVerificationError) Error ¶
func (e *WebhookVerificationError) Error() string