Official Go SDK for the SiftingIO market data API — REST plus a live WebSocket client, idiomatic and context-aware.
context.Contexteverywhere, functional options, typed structs.- Resource-mapped. Method names mirror the API docs 1:1.
- Batteries included. Auto-retry on 429/5xx, transparent gzip, cursor auto-pagination via Go 1.23 iterators, and an auto-reconnecting WebSocket client.
- One dependency —
github.com/coder/websocket(REST uses only the standard library).
go get github.com/siftingio/sdk-go@latestRequires Go 1.23+ (for range-over-func pagination).
package main
import (
"context"
"fmt"
"log"
"os"
siftingio "github.com/siftingio/sdk-go"
)
func main() {
client := siftingio.New(siftingio.WithAPIKey(os.Getenv("SIFTING_API_KEY")))
ctx := context.Background()
trade, err := client.Last.Trade(ctx, "crypto", "BTCUSD")
if err != nil {
log.Fatal(err)
}
fmt.Println(trade.Price, trade.Time)
profile, _ := client.Stocks.Profile(ctx, "AAPL")
fmt.Println(profile.Name, profile.SICDescription)
bars, _ := client.Crypto.Bars(ctx, "BTCUSD", &siftingio.BarsParams{Start: "2024-01-01", Interval: "1h"})
fmt.Printf("%d bars\n", len(bars.Data))
}The import path is
github.com/siftingio/sdk-go; the package name issiftingio. Add an explicit alias (siftingio "github.com/siftingio/sdk-go") if your tooling wants it.
client := siftingio.New(
siftingio.WithAPIKey("sft_..."), // X-API-Key header
siftingio.WithAPIKeyProvider(func(ctx context.Context) (string, error) { // dynamic alternative
return fetchToken(ctx)
}),
siftingio.WithBaseURL("https://api.sifting.io"), // override for proxies/staging
siftingio.WithWSURL("wss://stream.sifting.io/ws/v1"),
siftingio.WithHTTPClient(myHTTPClient), // custom *http.Client
siftingio.WithTimeout(30*time.Second), // per-request timeout (via context)
siftingio.WithMaxRetries(2), // retries for 429 / 5xx
siftingio.WithHeader("X-Trace", "..."), // header on every request
)| Field | Endpoints | Highlights |
|---|---|---|
client.Last |
/v1/last/* |
Trade, Quote, TVL — live snapshots |
client.Stocks |
/v1/fnd/stocks/*, /v1/hist/stocks/* |
Search, Profile, Filings, Financials, Ratios, Insiders, Events, Screener, Bars, … |
client.Filers |
/v1/fnd/filers/* |
Holdings — 13F positions |
client.Markets |
/v1/fnd/markets/* |
List, Status, Hours, Calendar |
client.Forex |
/v1/hist/forex/* |
Bars |
client.Crypto |
/v1/hist/crypto/* |
Bars |
client.Dex |
/v1/fnd/dex/* |
Wallet portfolios |
client.EconomicCalendar |
/v1/fnd/economic-calendar |
List |
List endpoints return a *ListResponse[T] with Meta.NextCursor. Stream every page with the AutoPaginate iterator (Go 1.23+):
for filing, err := range siftingio.AutoPaginate(ctx,
func(ctx context.Context, cursor string) ([]siftingio.Filing, string, error) {
page, err := client.Stocks.Filings(ctx, "AAPL", &siftingio.FilingsParams{Cursor: cursor, Form: "10-K"})
if err != nil {
return nil, "", err
}
return page.Data, page.Meta.NextCursor, nil
},
) {
if err != nil {
log.Fatal(err)
}
fmt.Println(filing.Accession, filing.FiledAt)
}siftingio.CollectAll(...) gathers all pages into a slice.
sock := client.WS() // siftingio.WithAutoReconnect(false) to disable
sock.OnTick(func(t *siftingio.Tick) { fmt.Println(t.Symbol, t.Price) })
sock.OnTVL(func(v *siftingio.TVLUpdate) { fmt.Println(v.Symbol, v.USD) })
sock.OnError(func(e *siftingio.WSError) { log.Println("server error:", e.Code, e.Message) })
sock.OnReconnect(func(attempt int) { log.Println("reconnecting", attempt) })
if err := sock.Connect(ctx); err != nil {
log.Fatal(err)
}
defer sock.Close()
sock.Subscribe(ctx, siftingio.ProductCEX, "BTCUSD", "ETHUSD") // cex|dex|fx|us|tvl
sock.Subscribe(ctx, siftingio.ProductTVL, "eth:WETH-USDC")
<-ctx.Done()Subscriptions are tracked and replayed automatically on reconnect. Handlers run on the read goroutine — keep them quick or hand work to your own channels/goroutines.
trade, err := client.Last.Trade(ctx, "crypto", "BTCUSD")
if err != nil {
var apiErr *siftingio.APIError
if errors.As(err, &apiErr) {
// apiErr.Status, apiErr.Code, apiErr.RetryAfter, apiErr.RequestID, apiErr.Body
}
var connErr *siftingio.ConnectionError
if errors.As(err, &connErr) {
// connErr.Timeout, connErr.Err
}
}The client automatically retries 429 and 5xx up to WithMaxRetries, honoring Retry-After.
MIT