glassnode-go
Go client for Glassnode Basic API with 25 metric categories, typed structs, bulk endpoints, Point-in-Time data, and zero dependencies
About glassnode-go
This large README is shown as a plain-text preview. Read the full README at the source
# GlassNode Golang SDK

[](https://github.com/tigusigalpa/glassnode-go/actions/workflows/ci.yml)
[](https://github.com/tigusigalpa/glassnode-go/actions/workflows/test.yml)
[](https://golang.org/)
[](LICENSE)
[](https://github.com/tigusigalpa/glassnode-go/actions/workflows/codeql.yml)
[](https://codecov.io/gh/tigusigalpa/glassnode-go)
[](https://github.com/tigusigalpa/glassnode-go/releases)
[](https://pkg.go.dev/github.com/tigusigalpa/glassnode-go)
> **Unofficial** Go SDK for the [Glassnode Basic API](https://docs.glassnode.com/basic-api/). Not affiliated with or
> endorsed by Glassnode — just a community-built tool I wish had existed when I started working with on-chain data.
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.
If you've ever found yourself hand-crafting `net/http` calls to Glassnode, parsing JSON into `map[string]interface{}`,
and string-building metric paths at 2 AM to ship a dashboard — you already know it gets tedious fast. This SDK wraps all
of that into a clean, idiomatic Go package with strongly-typed structs, `context.Context` propagation, and sensible
defaults, so you can spend your time on application logic instead of HTTP plumbing.
The SDK exposes the complete documented metric surface through three layers of API, each one building on the last:
1. **Typed transport/config** — a `Client` with functional options, secure defaults, `context.Context` support, and zero
external dependencies
2. **Metadata & generic metrics** — a `MetadataService` for runtime discovery and a `MetricsService` that works with any
present or future metric path
3. **Ergonomic category services** — 25 typed service structs with convenience methods that map 1:1 to Glassnode's
endpoint categories
## Who Is This For?
This SDK was written for the kind of developer who:
- Builds trading dashboards, research tools, or backtesting pipelines that need on-chain data
- Cares about dependency hygiene — no surprise transitive packages, no `go.sum` bloat
- Wants errors they can actually branch on with `errors.Is` / `errors.As` instead of grepping strings
- Runs services in production and needs concurrency-safe clients that won't fall over under load
- Likes their libraries to do the boring stuff (retries, redaction, context propagation) so they can focus on the
interesting stuff
If that sounds like you, welcome — you're in the right place.
## Why This SDK?
- **Zero external dependencies** — uses only the Go standard library, so it won't bloat your `go.sum` or create
transitive dependency conflicts that surface at the worst possible moment
- **Idiomatic Go** — functional options, `context.Context` propagation, strongly-typed structs, and `errors.Is` /
`errors.As` support throughout
- **Concurrency-safe** — the `Client` is safe for concurrent use across goroutines, perfect for high-throughput services
and fan-out workloads
- **Discover metrics at runtime** — not sure which parameters a metric accepts? Ask the metadata API before you make
data calls and avoid wasting credits on bad requests
- **Survive rate limits gracefully** — automatic retry on 429 with server-aware backoff, so you don't have to wrap every
call in your own retry loop
- **Keep your API key safe** — header-based auth by default, and the SDK redacts the key in request URLs and response metadata
so it never leaks into your logs
- **Only pay for what you use** — bulk endpoints with explicit asset lists put you in control of credit consumption, no
accidental wildcard blow-ups
## Features
- **Zero external dependencies** — uses only the Go standard library, nothing else. Your `go.sum` stays as short as the
day you started the project
- **25 category services** with typed convenience methods covering every documented endpoint category
- **Generic metric API** for every valid metric path — present or future, even ones not yet wrapped by a typed method
- **Metadata-first** — discover assets, metric paths, and parameter capabilities at runtime before making data calls
- **Bulk endpoint support** with repeated query parameters for multi-asset requests
- **Point-in-Time metrics** with `computed_at` timestamp preservation for historically accurate analysis and backtesting
- **Header authentication** (`X-Api-Key`) by default; query-string opt-in for the rare environments that genuinely need
it
- **Automatic retry** on HTTP 429 with `x-rate-limit-reset` support and exponential backoff as a fallback
- **Exported, inspectable error types** with `errors.Is` / `errors.As` support — no string matching required
- **API key redaction** in request URLs and response metadata, including query-string authentication
- **Concurrency-safe** `Client` suitable for goroutine use without additional locking
- **Configurable HTTP transport** for testing and custom `http.RoundTripper` implementations (proxies, middleware,
recording)
- **Functional options** — configure only what you need, sensible defaults for everything else
## Installation
```bash
go get github.com/tigusigalpa/glassnode-go
```
That's it. The module has zero external dependencies, so your `go.sum` stays clean. Just import it and start using it —
no `go mod tidy` surprises, no version conflicts to untangle.
## Quick Start
Here's a complete, runnable example — create a client, fetch BTC price data, and print it. Drop it into a `main.go` and
you're off:
```go
package main
import (
"context"
"fmt"
"log"
glassnode "github.com/tigusigalpa/glassnode-go"
)
func main() {
// From environment variable GLASSNODE_API_KEY
client, err := glassnode.NewClientFromEnv()
if err != nil {
log.Fatal(err)
}
// Or pass an explicit key if you prefer
// client := glassnode.NewClient("YOUR_API_KEY")
ctx := context.Background()
// Fetch BTC price with 24h resolution
price, err := client.Market.Price(ctx, &glassnode.MetricQuery{
Asset: "BTC",
Resolution: glassnode.Resolution24h,
})
if err != nil {
log.Fatal(err)
}
for _, p := range price {
fmt.Printf("BTC price at %d: $%.2f\n", p.T, p.V)
}
}
```
Want to explore what's available before pulling data? The metadata API is your friend — it lets you window-shop metrics
without spending credits on guesses:
```go
// List all supported assets
assets, err := client.Metadata.Assets(ctx, "")
if err != nil {
log.Fatal(err)
}
for _, a := range assets {
fmt.Printf("%s (%s)\n", a.Name, a.Symbol)
}
// Inspect a specific metric's parameters
metricInfo, err := client.Metadata.Metric(ctx, "/market/price_usd")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Metric: %s\n", metricInfo.Path)
fmt.Printf("Parameters: %v\n", metricInfo.Parameters)
```
### Common Use Cases
A few real-world patterns to get you oriented. These are intentionally small — copy, paste, and adapt.
**Building a market dashboard:**
```go
ctx := context.Background()
// Get OHLC candles for charting
ohlc, err := client.Market.PriceOHLC(ctx, &glassnode.MetricQuery{
Asset: "BTC",
Resolution: glassnode.Resolution1h,
})
// Market cap and realized cap for valuation analysis
mcap, err := client.Market.MarketCap(ctx, &glassnode.MetricQuery{Asset: "BTC"})
rcap, err := client.Market.RealizedCap(ctx, &glassnode.MetricQuery{Asset: "BTC"})
// MVRV ratio — a classic cycle indicator
mvrv, err := client.Indicators.MVRV(ctx, &glassnode.MetricQuery{Asset: "BTC"})
```
**Monitoring network health:**
```go
// Active addresses — measures network usage
active, err := client.Addresses.ActiveCount(ctx, &glassnode.MetricQuery{
Asset: "BTC",
Resolution: glassnode.Resolution24h,
})
// Hash rate — mining security
hashrate, err := client.Mining.HashRate(ctx, &glassnode.MetricQuery{Asset: "BTC"})
// Total supply — track inflation
supply, err := client.Supply.CirculatingSupply(ctx, &glassnode.MetricQuery{Asset: "BTC"})
```
**Concurrent fetches with goroutines:**
The `Client` is concurrency-safe, so fan-out patterns just work. Here's the textbook pattern for pulling several metrics
in parallel:
```go
var (
price []glassnode.TimePoint
sopr []glassnode.TimePoint
active []glassnode.TimePoint
)
var wg sync.WaitGroup
wg.Add(3)
query := &glassnode.MetricQuery{Asset: "BTC", Resolution: glassnode.Resolution24h}
go func() {
defer wg.Done()
price, _ = client.Market.Price(ctx, query)
}()
go func() {
defer wg.Done()
sopr, _ = client.Indicators.SOPR(ctx, query)
}()
go func() {
defer wg.Done()
active, _ = client.Addresses.ActiveCount(ctx, query)
}()
wg.Wait()
// The Client is concurrency-safe — no additional locking needed
```
> **Tip:** In production code, propagate errors back from each goroutine through a channel or shared `error` slice
> instead of discarding them with `_`. The example above keeps things short; real services should never swallow errors
> silently.
**Tracking credit usage over time:**
```go
usage, err := client.User.APIUsage(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Credits used this month: %d\n", usage.CreditsUsed)
```
## API Key Security
Your Glassnode API key is the gateway to your account and its data credits. Treat it like a password — because it
effectively is one.
- **Never hardcode** your API key in source code or commit it to version control. Secrets in git history are forever
- Use `NewClientFromEnv()` to read from the `GLASSNODE_API_KEY` environment variable — this is the recommended path for
almost every deployment
- Or pass the key at runtime: `glassnode.NewClient(os.Getenv("GLASSNODE_API_KEY"))`
- The SDK **redacts** the API key in request URLs and response metadata, including when query-string authentication is
enabled
- **Header mode** (`X-Api-Key`) is the default and recommended; the key is sent in an HTTP header and never appears in
URLs, access logs, or proxy traces
- **Query-string mode** (`api_key`) is opt-in via `WithAuthMode(AuthModeQuery)` for environments where headers aren't
supported, but it's less secure — the key ends up in URLs that get logged everywhere they pass through
- If you accidentally expose a key, rotate it immediately
in [Glassnode Studio settings](https://studio.glassnode.com/settings/api). Don't wait, don't hope nobody noticed
## Authentication Modes
The SDK supports two authentication modes. Header mode is the default and recommended for all production usage — there's
rarely a good reason to switch:
```go
// Header mode (default, recommended)
// Sends the key as an X-Api-Key header — invisible in URLs and logs
client := glassnode.NewClient("key")
// Query-string mode (opt-in, less secure)
// Appends ?api_key=... to every request URL
client := glassnode.NewClient("key", glassnode.WithAuthMode(glassnode.AuthModeQuery))
```
## Configuration
The SDK uses the functional options pattern — configure only what you need, and everything else gets sensible defaults.
This keeps the common case a one-liner while still letting you tune behavior when you need to:
```go
client := glassnode.NewClient("key",
glassnode.WithBaseURL("https://api.glassnode.com"), // API base URL (rarely needs changing)
glassnode.WithTimeout(15*time.Second), // HTTP timeout
glassnode.WithRetry(3, time.Second), // Max retries + base delay for 429 backoff
glassnode.WithHTTPClient(customHTTPClient), // Custom *http.Client for testing or proxies
glassnode.WithUserAgent("my-app/1.0"), // User-Agent header
glassnode.WithAppID("trading-bot"), // Optional app identifier for Glassnode analytics
)
```
### Available Options
| Option | Default | Description |
|-----------------------|-----------------------------|--------------------------------------------------------------|
| `WithBaseURL(url)` | `https://api.glassnode.com` | API base URL |
| `WithTimeout(d)` | `30s` | HTTP request timeout |
| `WithRetry(n, delay)` | `1, 500ms` | Max retry attempts on 429 + base backoff delay |
| `WithHTTPClient(c)` | `&http.Client{Timeout: 30s}` | Custom HTTP client (for testing, proxies, custom transports) |
| `WithUserAgent(ua)` | `glassnode-go/1.0` | User-Agent header |
| `WithAppID(id)` | `""` | Optional app identifier |
| `WithAuthMode(mode)` | `AuthModeHeader` | `AuthModeHeader` or `AuthModeQuery` |
### A Note on Timeouts
The default 30s timeout is generous on purpose — some historical metric pulls return large payloads and take a moment.
If you're building a latency-sensitive service (say, a live trading dashboard), dial it down with `WithTimeout`. If
you're pulling years of daily data in a batch job, you may want to dial it up. The right value is the one that matches
your workload, not a magic number.
## Category Services
The 25 category services map 1:1 to Glassnode's documented endpoint categories. Each one exposes typed convenience
methods so you get autocomplete, compile-time checks, and self-documenting code:
| Service | Client Field | Documentation |
|---------------|-----------------------|-----------------------------------------------------------------------------|
| Addresses | `client.Addresses` | [Addresses](https://docs.glassnode.com/basic-api/endpoints/addresses) |
| Bridges | `client.Bridges` | [Bridges](https://docs.glassnode.com/basic-api/endpoints/bridges) |
| Blockchain | `client.Blockchain` | [Blockchain](https://docs.glassnode.com/basic-api/endpoints/blockchain) |
| Breakdowns | `client.Breakdowns` | [Breakdowns](https://docs.glassnode.com/basic-api/endpoints/breakdowns) |
| DeFi | `client.DeFi` | [DeFi](https://docs.glassnode.com/basic-api/endpoints/defi) |
| Derivatives | `client.Derivatives` | [Derivatives](https://docs.glassnode.com/basic-api/endpoints/derivatives) |
| Distribution | `client.Distribution` | [Distribution](https://docs.glassnode.com/basic-api/endpoints/distribution) |
| Entities | `client.Entities` | [Entities](https://docs.glassnode.com/basic-api/endpoints/entities) |
| ETH 2.0 | `client.ETH2` | [ETH 2.0](https://docs.glassnode.com/basic-api/endpoints/eth2) |
| Fees | `client.Fees` | [Fees](https://docs.glassnode.com/basic-api/endpoints/fees) |
| Global | `client.Global` | [Global](https://docs.glassnode.com/basic-api/endpoints/global) |
| Indicators | `client.Indicators` | [Indicators](https://docs.glassnode.com/basic-api/endpoints/indicators) |
| Institutions | `client.Institutions` | [Institutions](https://docs.glassnode.com/basic-api/endpoints/institutions) |
| Lightning | `client.Lightning` | [Lightning](https://docs.glassnode.com/basic-api/endpoints/lightning) |
| Macro | `client.Macro` | [Macro](https://docs.glassnode.com/basic-api/endpoints/macro) |
| Market | `client.Market` | [Market](https://docs.glassnode.com/basic-api/endpoints/market) |
| Mempool | `client.Mempool` | [Mempool](https://docs.glassnode.com/basic-api/endpoints/mempool) |
| Mining | `client.Mining` | [Mining](https://docs.glassnode.com/basic-api/endpoints/mining) |
| Options | `client.Options` | [Options](https://docs.glassnode.com/basic-api/endpoints/options) |
| Point-In-Time | `client.PointInTime` | [PIT](https://docs.glassnode.com/basic-api/endpoints/pit) |
| Protocols | `client.Protocols` | [Protocols](https://docs.glassnode.com/basic-api/endpoints/protocols) |
| Signals | `client.Signals` | [Signals](https://docs.glassnode.com/basic-api/endpoints/signals) |
| Supply | `client.Supply` | [Supply](https://docs.glassnode.com/basic-api/endpoints/supply) |
| Transactions | `client.Transactions` | [Transactions](https://docs.glassnode.com/basic-api/endpoints/transactions) |
| Treasuries | `client.Treasuries` | [Treasuries](https://docs.glassnode.com/basic-api/endpoints/treasuries) |
**Full endpoint coverage:** [docs/endpoint-coverage.md](docs/endpoint-coverage.md)
## Generic Metric API
The 25 category services cover every documented endpoint category, but Glassnode occasionally adds new metrics before a
typed wrapper exists. The generic `MetricsService` ensures you're never blocked waiting for an SDK release — it works
with **any** valid metric path, present or future:
```go
// Raw JSON — works with any metric path, returns []byte
// Use this when you want full control over decoding, or when the response shape is unusual
raw, err := client.Metrics.Get(ctx, "/addresses/sending_count", &glassnode.MetricQuery{
Asset: "BTC",
})
// Typed scalar time-series — parses into []TimePoint
// Use this when the metric returns {t, v} pairs
data, err := client.Metrics.GetTimePoints(ctx, "/indicators/sopr", &glassnode.MetricQuery{
Asset: "BTC",
Resolution: glassnode.Resolution24h,
})
// Typed object time-series — parses into []ObjectPoint
// Use this when the metric returns {t, o: {...}} pairs (e.g. OHLC)
ohlc, err := client.Metrics.GetObjectPoints(ctx, "/market/price_ohlc", &glassnode.MetricQuery{
Asset: "BTC",
Resolution: glassnode.Resolution1h,
})
```
### MetricQuery Parameters
The `MetricQuery` struct covers all common query parameters. Only `Asset` is required — the rest are optional and only
sent when set:
```go
type MetricQuery struct {
Asset string // Required — e.g. "BTC", "ETH"
Resolution Resolution // Optional — Resolution10m, Resolution1h, etc.
Since *int64 // Optional — Unix timestamp
Until *int64 // Optional — Unix timestamp
Currency Currency // Optional — CurrencyNative or CurrencyUSD
Format Format // Optional — FormatJSON or FormatCSV
TimestampFormat TimestampFormat // Optional — TimestampUnix or TimestampHumanized
}
```
Use `metadata/metric` to discover which parameters a specific metric accepts — not every metric honors every field, and
sending unsupported parameters can cause 400s.
### Convenience Resolution Constants
Instead of remembering string literals, use the exported constants for the common resolutions:
```go
glassnode.Resolution10m // "10m"
glassnode.Resolution1h // "1h"
glassnode.Resolution24h // "24h"
glassnode.Resolution1w // "1w"
glassnode.Resolution1month // "1month"
```
## Bulk Metrics
Frequently Asked Questions
What is glassnode-go?
glassnode-go is a Financial library for the Go programming language. Go client for Glassnode Basic API with 25 metric categories, typed structs, bulk endpoints, Point-in-Time data, and zero dependencies
How do I install glassnode-go?
Install glassnode-go with the Go module system using `go get tigusigalpa/glassnode-go`. Check the repository for the current installation instructions.
What category does glassnode-go belong to?
glassnode-go is listed under Financial, specifically Financial.