Stop Hand-Rolling HTTP Clients for Glassnode: Meet glassnode-go

go dev.to

Building with on-chain data is exciting right up until the integration work begins. A dashboard, research tool, alerting service, or backtesting pipeline may start with one metric, but it rarely stops there. Soon you are assembling request URLs by hand, decoding JSON into map[string]interface{}, checking every response for rate-limit headers, and adding retry logic that you hope will not become the next production incident.

That glue code is necessary, but it is not where a product becomes valuable. glassnode-go is designed to move that plumbing out of the way. It is an unofficial, community-built Go SDK for the Glassnode Basic API, aimed at developers who want a more idiomatic and production-oriented integration layer. It is not affiliated with or endorsed by Glassnode; it is a focused tool for teams that would rather build with on-chain data than continually maintain HTTP wrappers around it. 1

“A dependency-light, production-oriented Go module for the Glassnode Basic API, built with the kind of care you'd want from a library you depend on every day.” — the glassnode-go project README 1

The problem is not fetching a number—it's operating the integration

A raw API integration can fetch a price just fine. The difficult part begins when the application has to do it reliably: propagate cancellations through context.Context, protect the API key, distinguish bad input from an exhausted quota, tune timeouts, and retrieve multiple metrics concurrently without turning the client layer into a fragile tangle of helpers.

glassnode-go approaches that problem with a deliberately Go-native design. The module targets Go 1.21+ and declares no third-party module dependencies, keeping the integration limited to the standard library rather than adding transitive packages to the project. 1 Its client uses functional options for configuration, supports custom HTTP transports, and is documented as safe for concurrent use across goroutines. 1

What a data service needs What glassnode-go provides Why it matters
Familiar, discoverable access to standard metrics 25 typed category services such as Market, Addresses, Indicators, Mining, and Transactions Code is easier to navigate, review, and autocomplete. 1
A way to reach newly introduced or unusual endpoints A generic MetricsService for raw JSON, scalar time series, and object time series You do not have to wait for a typed wrapper to start experimenting. 1
Clarity before making requests Runtime metadata methods for assets, metric paths, and supported parameters Fewer invalid requests and less guesswork around endpoint capabilities. 1
Resilience under load Automatic handling of HTTP 429 responses and configurable retry behavior Rate-limit logic stays centralized instead of leaking into every call site. 1
Safer operations Header-based API-key authentication by default and redaction in URLs and error messages Secrets are less likely to appear in logs and diagnostics. 1

Start with a useful result, not a pile of boilerplate

Installation is a familiar Go command:

go get github.com/tigusigalpa/glassnode-go
Enter fullscreen mode Exit fullscreen mode

The quick-start experience is intentionally small. Put your Glassnode API key in GLASSNODE_API_KEY, create the client from the environment, and request the metric through a typed service. The following example retrieves BTC price data at daily resolution. 1

package main

import (
    "context"
    "fmt"
    "log"

    glassnode "github.com/tigusigalpa/glassnode-go"
)

func main() {
    client, err := glassnode.NewClientFromEnv()
    if err != nil {
        log.Fatal(err)
    }

    ctx := context.Background()
    price, err := client.Market.Price(ctx, &glassnode.MetricQuery{
        Asset:      "BTC",
        Resolution: glassnode.Resolution24h,
    })
    if err != nil {
        log.Fatal(err)
    }

    for _, point := range price {
        fmt.Printf("BTC price at %d: $%.2f\n", point.T, point.V)
    }
}
Enter fullscreen mode Exit fullscreen mode

The important detail is not merely that the example is short. It encodes a useful boundary: application code talks in terms of market data and queries, while the SDK takes responsibility for building the request, applying authentication, decoding the response, and returning a typed time series. That separation makes a dashboard handler, scheduled research job, or backtest easier to test and evolve.

Typed when you can; generic when you need to

The primary API surface is made of category services that mirror Glassnode's documented endpoint families. For example, a dashboard can request OHLC data from client.Market.PriceOHLC, activity data from client.Addresses.ActiveCount, and an indicator such as SOPR from client.Indicators.SOPR. The repository maintains an endpoint-coverage document that maps API paths to the corresponding SDK methods. 1

But strongly typed wrappers should never become a release-cycle bottleneck. APIs evolve, new metrics appear, and some response shapes are inherently specialized. For those moments, glassnode-go supplies the generic MetricsService. It can return raw JSON through Get, scalar time-series data through GetTimePoints, or object time-series data through GetObjectPoints. 1

// Use a valid metric path even when you do not need a dedicated convenience method.
data, err := client.Metrics.GetTimePoints(ctx, "/indicators/sopr", &glassnode.MetricQuery{
    Asset:      "BTC",
    Resolution: glassnode.Resolution24h,
})
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Received %d SOPR observations\n", len(data))
Enter fullscreen mode Exit fullscreen mode

That combination is especially useful in real products. Your stable, frequently used calls can remain concise and self-documenting; exploratory work, internal analytics, and recently introduced paths can still proceed without compromising the client architecture.

Discover capabilities before you spend requests

Metric paths alone do not tell the whole story. A query may support different resolutions, assets, bulk access, currencies, or time formats depending on the underlying endpoint. Guessing at those parameters often results in failed calls that are hard to diagnose in a busy service.

glassnode-go exposes a metadata layer for exactly this reason. The MetadataService can list supported assets, discover metric paths, and inspect a metric's available parameters at runtime. The SDK documentation recommends this metadata-first workflow before making data calls. 1

metric, err := client.Metadata.Metric(ctx, "/market/price_usd")
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Metric: %s\n", metric.Description)
fmt.Printf("Supported resolutions: %v\n", metric.Resolutions)
Enter fullscreen mode Exit fullscreen mode

For a product team, this is more than a convenience. It lets a configuration screen, query builder, or data pipeline validate its choices against the API instead of embedding assumptions that eventually become stale.

Rate limits deserve first-class engineering

A high-quality SDK should make the correct behavior the default behavior. In glassnode-go, HTTP 429 responses trigger automatic retries for idempotent GET requests. The client uses the x-rate-limit-reset header when the server supplies it and falls back to exponential backoff when it does not. It avoids retrying 400, 401, and 404 responses because those represent problems a retry is unlikely to fix. Retry behavior can be configured with WithRetry. 1

This decision is quietly powerful. Every endpoint call retains the same clean shape, while the behavior that protects the service is standardized in one place. When the retry budget is exhausted, the caller still gets actionable context through the exported APIError type and the ErrRateLimited sentinel. 1

_, err := client.Indicators.SOPR(ctx, &glassnode.MetricQuery{Asset: "BTC"})
if err != nil && errors.Is(err, glassnode.ErrRateLimited) {
    var apiErr *glassnode.APIError
    if errors.As(err, &apiErr) {
        log.Printf("Rate limit exhausted; reset in %ds", apiErr.RateLimitReset)
    }
}
Enter fullscreen mode Exit fullscreen mode

The same philosophy applies to the rest of the error model. The package exposes sentinel errors for conditions such as bad requests, unauthorized access, missing metric paths, rate limits, and internal server errors. That gives callers the familiar errors.Is and errors.As workflow rather than forcing production code to branch on error-message strings. 1

Security should not be an afterthought

An API key is an operational secret, not a configuration string that belongs in source code or a query URL. The library defaults to the X-Api-Key header mode and supports query-string authentication only as an explicit opt-in for exceptional environments. It also documents API-key redaction in URLs, response metadata, and error messages. 1

The recommended developer experience is therefore straightforward: keep the key in GLASSNODE_API_KEY, construct the client with NewClientFromEnv(), and let the client carry the secret in headers. It is a small convention with a meaningful payoff in log hygiene and incident response.

Built for dashboards today—and historical research tomorrow

The SDK is not limited to a single-metric dashboard. Its BulkQuery support can request data for explicitly specified multiple assets in one call, reducing round trips where the underlying metric supports bulk access. The project documentation also notes that the cost model remains per asset, so bulk requests optimize throughput rather than magically reducing credit consumption. 1

For researchers, the Point-in-Time API is another standout capability. PIT response models preserve a computed_at timestamp alongside the observation timestamp. This matters when historical metric values may later be revised: a backtest can distinguish what the current dataset says from what was known at a particular historical moment. The SDK offers typed PIT response models and generic point-in-time methods for that workflow. 1

Use case A practical glassnode-go path
Market dashboard Typed Market, Indicators, and Addresses services for the metrics displayed most often. 1
Multi-asset research job Metrics.GetBulk with explicit asset lists, plus metadata checks before the request. 1
API explorer or no-code query builder Metadata.Assets, Metadata.Metrics, and Metadata.Metric to populate valid options dynamically. 1
Strategy backtesting Point-in-Time response models that retain computed_at for historically aware analysis. 1
Custom or newly added endpoint Metrics.Get, GetTimePoints, or GetObjectPoints with a valid metric path. 1

The developer experience is the feature

glassnode-go does not try to hide Glassnode behind a proprietary abstraction. Instead, it gives Go developers a reliable, conventional way to work with the Basic API: typed services for everyday endpoints, generic access when flexibility is needed, metadata for discovery, and production-minded defaults for context propagation, concurrency, security, and retries. The project's initial v1.0.0 release documents these capabilities along with typed response models, configurable transport options, and a mocked-transport test suite. 2

If your next Go project needs on-chain metrics, take the SDK for a spin, browse the examples, and consider contributing an issue, documentation improvement, or pull request. The repository is MIT licensed and welcomes contributions. 1

Repository: github.com/tigusigalpa/glassnode-go

Note: This post discusses developer tooling for accessing data. It is not investment, trading, or financial advice.

References

Source: dev.to

arrow_back Back to Tutorials