Go client library for the Sparkscan API (v3.4.0) -- an explorer for Spark wallets on Bitcoin.
- Fully generated from the OpenAPI spec using ogen -- update the spec and run
go generate - All 19 endpoints covered across v1 and v2 APIs (transactions, addresses, tokens, historical data, stats)
- Pagination iterators using Go 1.23+
iter.Seq2for both offset-based (v1) and cursor-based (v2) endpoints - Functional options for API key auth, custom HTTP clients, and timeouts
go get github.com/refrakts/sparkscan-api-goRequires Go 1.25+.
package main
import (
"context"
"fmt"
"log"
sparkscan "github.com/refrakts/sparkscan-api-go"
"github.com/refrakts/sparkscan-api-go/ogen"
)
func main() {
client, err := sparkscan.NewClient("https://api.sparkscan.io")
if err != nil {
log.Fatal(err)
}
stats, err := client.GetV2StatsSummary(context.Background(), ogen.GetV2StatsSummaryParams{
Network: ogen.GetV2StatsSummaryNetworkMAINNET,
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Active accounts: %d\n", stats.ActiveAccounts)
fmt.Printf("TVL: $%.2f\n", stats.TotalValueLockedUsd)
fmt.Printf("BTC price: $%.2f\n", stats.CurrentBtcPriceUsd)
}client, err := sparkscan.NewClient("https://api.sparkscan.io",
sparkscan.WithAPIKey("your-api-key"),
sparkscan.WithHTTPClient(customClient),
sparkscan.WithTimeout(10 * time.Second),
)// Latest transactions
txs, err := client.GetV1TxLatest(ctx, ogen.GetV1TxLatestParams{
Limit: ogen.NewOptInt(10),
})
// Single transaction by ID
tx, err := client.GetV1TxByTxid(ctx, ogen.GetV1TxByTxidParams{
Txid: "abc123...",
})// Address summary
info, err := client.GetV1AddressByAddress(ctx, ogen.GetV1AddressByAddressParams{
Address: "sp1...",
})
// Token holdings
tokens, err := client.GetV1AddressByAddressTokens(ctx, ogen.GetV1AddressByAddressTokensParams{
Address: "sp1...",
})Paginated endpoints have List* methods returning iter.Seq2 iterators that handle page fetching automatically:
// Offset-based (v1) -- iterates all pages
params := ogen.GetV1AddressByAddressTransactionsParams{Address: "sp1..."}
for tx, err := range client.ListV1AddressByAddressTransactions(ctx, params) {
if err != nil {
log.Fatal(err)
}
fmt.Println(tx.ID, tx.Type)
}
// Cursor-based (v2) -- iterates all pages
params := ogen.GetV2TokensListParams{Network: ogen.GetV2TokensListNetworkMAINNET}
for token, err := range client.ListV2TokensList(ctx, params) {
if err != nil {
log.Fatal(err)
}
fmt.Println(token.Name)
}For single-page access, use the non-List variant (e.g. GetV1AddressByAddressTransactions) which returns the raw paginated response including metadata.
result, err := client.GetV1TokensByIdentifier(ctx, ogen.GetV1TokensByIdentifierParams{
Identifier: "token-id",
})
// Response is a union: single detail or search results
if detail, ok := result.GetGetV1TokensByIdentifierOK0(); ok {
fmt.Printf("%s: supply %s\n", detail.Metadata.Name, detail.TotalSupply)
}params := ogen.GetV2HistoricalSatsBalancesByAddressParams{
Address: "sp1...",
Network: ogen.GetV2HistoricalSatsBalancesByAddressNetworkMAINNET,
}
for point, err := range client.ListV2HistoricalSatsBalancesByAddress(ctx, params) {
if err != nil {
log.Fatal(err)
}
fmt.Printf("t=%d balance=%s\n", point.T, point.B)
}API errors are returned as *sparkscan.APIError:
tx, err := client.GetV1TxByTxid(ctx, params)
if err != nil {
var apiErr *sparkscan.APIError
if errors.As(err, &apiErr) {
fmt.Printf("API error %d: %s\n", apiErr.StatusCode, apiErr.Message)
}
}For advanced use cases, access the underlying ogen client directly:
raw := client.Raw()
// Call ogen methods with full response type switchingAll v1 and v2 endpoints are supported:
| Domain | Endpoints |
|---|---|
| Transactions | GetV1TxLatest, GetV1TxByTxid, GetV2TxLatest, GetV2TxByTxid |
| Addresses | GetV1AddressByAddress, GetV1AddressByAddressTransactions, GetV1AddressByAddressTokens, GetV2AddressByAddressTransactions |
| Tokens | GetV1TokensByIdentifier, GetV1TokensByIdentifierHolders, GetV1TokensByIdentifierTransactions, PostV1TokensMetadataBatch, PostV1TokensIssuerLookup, GetV2TokensList |
| Historical | GetV2HistoricalSatsBalancesByAddress, GetV2HistoricalTokensBalancesByAddressByTokenIdentifier, GetV2HistoricalNetWorthByAddress |
| Stats | GetV2StatsSummary, GetV2StatsTpv |
Paginated endpoints also have List* iterator variants.
Everything regenerates from the OpenAPI spec:
go generate ./...This runs three steps:
cmd/normalize-spec-- pre-processes the spec to fix ogen-incompatible patterns (anyOf unions, invalid types, merges multi-object variants)ogen-- generates the raw Go client and types inogen/cmd/generate-wrapper-- generateswrapper_gen.gowith error-handling methods and pagination iterators
All generated code is checked in so consumers can go get without running generation.
client.go Hand-written: NewClient, options, auth, error types
options.go Hand-written: WithAPIKey, WithHTTPClient, WithTimeout
pagination.go Hand-written: PaginateOffset, PaginateCursor helpers
wrapper_gen.go Generated: endpoint methods + List* iterators
ogen/ Generated: raw ogen client, types, JSON codecs
api/schema.json Upstream OpenAPI spec (input)
cmd/normalize-spec/ Schema pre-processor
cmd/generate-wrapper/ Wrapper code generator