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 Go SDK is the same library the operator CLI uses internally, so every endpoint exposed by the API is reachable through it.

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:  "rek_…", // 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.Asset, 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.HistoryQuery{
    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.TS.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. The SDK handles reconnect with last-event-id resume; cancel via the parent ctx.

events, err := c.PriceStream(ctx, client.PriceStreamQuery{
    Asset: "native",
    Quote: "fiat:USD",
})
if err != nil {
    return err
}
for ev := range events {
    if ev.Err != nil {
        log.Printf("stream error: %v", ev.Err)
        continue
    }
    fmt.Printf("[%s] %s = %s\n",
        ev.Bucket.Format(time.RFC3339), ev.Asset, ev.Price)
}

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",
    Interval: "1h",
})
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