Skip to main content

Go SDK

Idiomatic Go client for the Stellar Index API

Typed, SemVer-stable, no surprises. Anonymous mode for the public tier; bearer-token mode for paid tiers and SEP-10 JWTs. The SDK covers the pricing/read surface — prices, history, OHLC, markets, the asset catalogue, and account self-service — with ~36 typed methods; SSE streams and the explorer read surface are reachable over plain HTTP.

Install

Single dependency. The module path follows the canonical github.com/StellarIndex/stellar-index repo path.

go get github.com/StellarIndex/stellar-index/pkg/client

Quick start

One-asset current-price lookup. Anonymous works at the public rate-limit; pass APIKey to bump to your tier's budget.

package main

import (
    "context"
    "fmt"
    "github.com/StellarIndex/stellar-index/pkg/client"
)

func main() {
    c := client.New(client.Options{
        BaseURL: "https://api.stellarindex.io",
        APIKey:  "sip_…", // optional; anonymous works at the public rate-limit
    })

    p, err := c.Price(context.Background(), client.PriceQuery{
        Asset: "native",
        Quote: "fiat:USD",
    })
    if err != nil {
        panic(err)
    }
    fmt.Printf("XLM/USD = %s (%s, observed %s)\n",
        p.Data.Price, p.Data.PriceType, p.Data.ObservedAt)
}

Common patterns

Batch lookup — up to 1000 assets per call

Single round trip; the wire shape preserves the input order. Use this when feeding a watchlist or rendering a portfolio strip.

prices, err := c.PriceBatch(ctx, client.PriceBatchQuery{
    Assets: []string{
        "native",
        "USDC-GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN",
        "AQUA-GBNZILSTVQZ4R7IKQDGHYGY2QXL5QOFJYQMXPKWRRM5PAV7Y4M67AQUA",
    },
    Quote: "fiat:USD",
})
if err != nil {
    return err
}
for _, p := range prices.Data {
    fmt.Printf("%-10s %s\n", p.AssetID, p.Price)
}

Trade history — recent trades for a pair

Cursor-paginated. For a one-shot recent-trades panel, take the first page; for a backfill or aggregator, follow Pagination.Next until empty.

h, err := c.History(ctx, client.HistoryRangeQuery{
    Base:  "native",
    Quote: "USDC-GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN",
    Limit: 50,
})
if err != nil {
    return err
}
for _, t := range h.Data {
    fmt.Printf("%s  %s/%s  %s @ %s\n",
        t.Timestamp.Format(time.RFC3339), t.BaseAsset, t.QuoteAsset,
        t.BaseAmount, t.Price)
}

Closed-bucket SSE stream

Per ADR-0018 the API only emits closed buckets — every event is final. SSE sits deliberately outside the typed SDK surface (request/response only) — consume the stream with net/http directly; cancel via the parent ctx.

req, err := http.NewRequestWithContext(ctx, http.MethodGet,
    "https://api.stellarindex.io/v1/price/stream?asset=native&quote=fiat:USD", nil)
if err != nil {
    return err
}
req.Header.Set("Accept", "text/event-stream")

resp, err := http.DefaultClient.Do(req)
if err != nil {
    return err
}
defer resp.Body.Close()

sc := bufio.NewScanner(resp.Body)
for sc.Scan() {
    if data, ok := strings.CutPrefix(sc.Text(), "data: "); ok {
        fmt.Println(data) // one closed VWAP bucket per event, JSON
    }
}

OHLC bar — single-window summary

For per-asset cards or sparkline backing data. Pair with /v1/chart for multi-bar series; OHLC is the one-bar variant.

o, err := c.OHLC(ctx, client.OHLCQuery{
    Base:  "native",
    Quote: "fiat:USD",
    // From/To zero → the server defaults to the last hour,
    // clamped to a closed-bucket boundary (ADR-0015).
})
if err != nil {
    return err
}
fmt.Printf("O=%s H=%s L=%s C=%s vol=%s\n",
    o.Data.Open, o.Data.High, o.Data.Low, o.Data.Close, o.Data.QuoteVolume)

Error handling — *APIError wraps problem+json

HTTP errors from the server come through as `*client.APIError` carrying the problem-document fields (type / title / status / detail). Network / parse errors come through wrapped via fmt.Errorf — distinguish with errors.As.

_, err := c.Price(ctx, client.PriceQuery{Asset: "garbage"})
if err != nil {
    var apiErr *client.APIError
    if errors.As(err, &apiErr) {
        switch apiErr.Status {
        case 404:
            // pair not yet observed — render "no price"
        case 400:
            // bad asset id — fix call site
        default:
            log.Printf("api error: %d %s", apiErr.Status, apiErr.Detail)
        }
    } else {
        log.Printf("transport error: %v", err)
    }
}

Authentication

Three modes mirror the server's auth middleware:

Anonymous
No APIKey on the client. Rate-limited per IP. Good for prototyping and embedded widgets.
API key
Set Options.APIKey. Sent as Authorization: Bearer on every request. Sign in at /signin (magic-link, no password) and mint a key from /account.
SEP-10
Verified at /v1/auth/sep10/{challenge,token}. Pass the resulting JWT as Options.APIKey; the SDK forwards it verbatim.

Reference