nansen-go
Go client for Nansen AI API with Smart Money analytics, token screener, profiler, and zero dependencies
About nansen-go
Nansen AI Golang Client/SDK/Library

A Go client for the Nansen AI API that tries to stay out of your way. No third-party dependencies, no surprises β just the standard library and an API that feels like the rest of your Go code.
Package: pkg.go.dev/github.com/tigusigalpa/nansen-go
Why you might like it
- Nothing to vendor. The whole thing is built on the standard library.
go getit and you're done β no dependency tree to audit. - Contexts everywhere. Every call takes a
context.Contextfirst, so timeouts and cancellation work exactly the way you'd expect. - Configured with options, not structs.
WithBaseURL,WithHTTPClient,WithTimeout,WithRetryβ mix and match what you need. - Safe to share. Create one client and hand it to as many goroutines as you like. No locks, no fuss.
- Typed on purpose. Chains, sort fields, trader types, and labels are real constants. Optional request fields are
pointers, so a stray
falseor0never sneaks into your JSON. - Errors you can actually inspect. Failures come back as an
*APIErrorwith the status code, message, raw body, and rate-limit headers β and they play nicely witherrors.Is. - Retries when you want them. Opt in with
WithRetryand the client backs off exponentially on 429s, honoringRetry-After,RateLimit-Reset, andX-RateLimit-Resetalong the way.
Installation
go get github.com/tigusigalpa/nansen-go
Quick Start
package main
import (
"context"
"fmt"
"log"
"os"
"time"
"github.com/tigusigalpa/nansen-go"
)
func main() {
client, err := nansen.New(os.Getenv("NANSEN_API_KEY"),
nansen.WithTimeout(30*time.Second),
nansen.WithRetry(3, 500*time.Millisecond, 5*time.Second),
)
if err != nil {
log.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
tf := nansen.Timeframe24H
resp, err := client.TokenGodMode.TokenScreener(ctx, &nansen.TokenScreenerRequest{
Chains: []nansen.Chain{nansen.ChainEthereum, nansen.ChainSolana},
Timeframe: &tf,
Pagination: &nansen.PaginationRequest{
Page: nansen.IntPtr(1),
PerPage: nansen.IntPtr(20),
},
})
if err != nil {
log.Fatal(err)
}
for _, t := range resp.Data {
fmt.Printf("%s on %s\n", t.TokenSymbol, t.Chain)
}
}
You'll need a NANSEN_API_KEY β grab one from Nansen.
Configuring the client
Everything is optional. Pass only the options you care about; the rest fall back to sensible defaults.
client, err := nansen.New(apiKey,
nansen.WithBaseURL("https://api.nansen.ai"),
nansen.WithHTTPClient(&http.Client{Timeout: 10*time.Second}),
nansen.WithTimeout(30*time.Second),
nansen.WithRetry(3, 500*time.Millisecond, 5*time.Second),
)
| Option | Description |
|---|---|
WithBaseURL | Override the default base URL (https://api.nansen.ai). |
WithHTTPClient | Provide a custom *http.Client. |
WithTimeout | A default request timeout, used when your context doesn't already carry a deadline. |
WithRetry | Turns on automatic retries with exponential backoff. Takes max attempts, initial delay, and max delay. |
What's available
The API surface is split into services that hang off the client:
client.SmartMoneyβ netflows, current and historical holdings, and DEX trades.client.TokenGodModeβ the token screener, flow intelligence, and who-bought-sold.client.Profilerβ current balances, labels (including premium labels), and DEX trade history for an address.client.Portfolioβ DeFi holdings.client.Historicalβ the backtesting endpoints under/api/v1beta1/.
When things go wrong
Every API failure comes back as an *nansen.APIError, so you can dig into exactly what happened:
resp, err := client.SmartMoney.Netflow(ctx, req)
if err != nil {
var apiErr *nansen.APIError
if errors.As(err, &apiErr) {
fmt.Println(apiErr.StatusCode)
fmt.Println(apiErr.Message)
fmt.Println(apiErr.RawBody)
fmt.Println(apiErr.Headers.Get("RateLimit-Remaining"))
if apiErr.RetryAfter != nil {
fmt.Println("retry after:", *apiErr.RetryAfter)
}
if apiErr.RateLimitRemaining != nil {
fmt.Println("requests remaining:", *apiErr.RateLimitRemaining)
}
}
}
If you just want to branch on the kind of failure, reach for errors.Is and the built-in sentinels:
if errors.Is(err, nansen.ErrRateLimited) { /* ... */ }
if errors.Is(err, nansen.ErrUnauthorized) { /* ... */ }
if errors.Is(err, nansen.ErrNotFound) { /* ... */ }
About retries
Once you've called WithRetry, the client quietly retries a few situations for you:
- 429 Too Many Requests β it waits according to
Retry-AfterorRateLimit-Reset/X-RateLimit-Reset, and stashesRateLimit-Remaining/X-RateLimit-Remainingon the returnedAPIErrorin case you want to peek at your remaining quota. - Transient 5xx responses β the kind that usually clear up on a second try.
- Flaky network errors β unless your context has already been cancelled.
Backoff grows exponentially but never exceeds the max delay you set. One thing worth knowing: your timeout budget (from
WithTimeout or a deadline on the context) covers all the attempts together, not each one on its own β so you
always stay within the bound you asked for.
Optional fields and pointer helpers
Optional request fields are pointers, which means false, 0, and empty strings only get sent when you actually mean
them:
req := &nansen.TokenScreenerRequest{
Chains: []nansen.Chain{nansen.ChainSolana},
Filters: &nansen.TokenScreenerFilters{
IncludeStablecoins: nansen.BoolPtr(false),
MarketCapUSD: &nansen.NumericRangeFilter{
Min: nansen.Float64Ptr(1_000_000),
Max: nansen.Float64Ptr(50_000_000),
},
},
}
To keep that from getting tedious, there are little helpers for the common types:
StringPtr(s string) *stringIntPtr(i int) *intBoolPtr(b bool) *boolFloat64Ptr(f float64) *float64
Examples
If you'd rather learn by running something, the examples directory has a few complete programs:
examples/screenerβ the token screener with filters and sorting.examples/profilerβ address balances and DEX trade history.examples/smart_moneyβ Smart Money netflows, holdings, and DEX trades.
Point one at your API key and go:
NANSEN_API_KEY=your_api_key go run ./examples/screener
Address labels
Use the endpoint-specific ProfilerLabelsChain type for labels. The regular endpoint excludes premium labels; use
AddressPremiumLabels when those classifications are required.
labels, err := client.Profiler.AddressLabels(ctx, &nansen.ProfilerAddressLabelsRequest{
Address: "0x0000000000000000000000000000000000000000",
Chain: nansen.ProfilerLabelsChainEthereum,
})
if err != nil {
log.Fatal(err)
}
for _, label := range labels.Data {
fmt.Println(label.Label)
}
Historical Smart Money holdings
HistoricalHoldings is a v1 daily-snapshot endpoint. It is distinct from the v1beta1 as_of_date backtesting
endpoint. Its economic fields use ExactNumber, preserving the provider's numeric JSON lexeme; check Present and
Null before reading Lexeme when absent, null, and zero must remain distinct.
resp, err := client.SmartMoney.HistoricalHoldings(ctx, &nansen.SmartMoneyHistoricalHoldingsRequest{
DateRange: nansen.DateOnlyRange{From: "2026-09-01", To: "2026-09-30"},
Chains: []nansen.SmartMoneyHistoricalHoldingsChain{nansen.HistoricalHoldingsChainEthereum},
})
if err != nil {
log.Fatal(err)
}
for _, holding := range resp.Data {
fmt.Printf("%s %s: %s\n", holding.Date, holding.TokenSymbol, holding.ValueUSD.Lexeme)
}
Exact numeric request filters
The legacy NumericRangeFilter remains available for compatibility. For filters where a decimal must reach Nansen
without a float64 conversion, use the additive Exactβ¦Request methods and ExactNumericRangeFilter. Bounds are
validated JSON-number lexemes; null is rejected because the documented range contracts do not support null bounds.
| Exact method | Exact filter type |
|---|---|
Profiler.AddressCurrentBalanceExact | ProfilerAddressBalancesExactFilters |
SmartMoney.NetflowExact | SmartMoneyNetflowExactFilters |
SmartMoney.HoldingsExact | SmartMoneyHoldingsExactFilters |
SmartMoney.HistoricalHoldingsExact | SmartMoneyHistoricalHoldingsExactFilters |
min, err := nansen.ExactNumberFromLexeme("0.12345678901234567890123456789")
if err != nil {
log.Fatal(err)
}
resp, err := client.SmartMoney.HoldingsExact(ctx, &nansen.SmartMoneyHoldingsExactRequest{
Chains: []nansen.Chain{nansen.ChainEthereum},
Filters: &nansen.SmartMoneyHoldingsExactFilters{
ValueUSD: &nansen.ExactNumericRangeFilter{Min: min},
},
})
_ = resp
if err != nil {
log.Fatal(err)
}
The exact current-balance and Smart Money schemas are documented by Nansen under /api/profiler/address-current-balances,
/api/smart-money/netflows, /api/smart-money/holdings, and /api/smart-money/historical-holdings.
Endpoints covered
Smart Money
POST /api/v1/smart-money/netflowPOST /api/v1/smart-money/holdingsPOST /api/v1/smart-money/historical-holdingsPOST /api/v1/smart-money/dex-trades
Token God Mode & Screener
POST /api/v1/token-screenerPOST /api/v1/tgm/flow-intelligencePOST /api/v1/tgm/who-bought-sold
Profiler
POST /api/v1/profiler/address/current-balancePOST /api/v1/profiler/address/labelsPOST /api/v1/profiler/address/premium-labelsPOST /api/v1/profiler/dex-trades
Portfolio
POST /api/v1/portfolio/defi-holdings
Historical Data (Backtesting)
POST /api/v1beta1/tgm/historical-token-flow-summaryPOST /api/v1beta1/smart-money/historical-token-balances
Testing
There's a unit test suite covering the retry/backoff logic, error mapping, and option validation. Run it the usual way:
go test ./...
License
MIT Β© Igor Sazonov. See LICENSE.
Frequently Asked Questions
What is nansen-go?
nansen-go is a Third-party APIs library for the Go programming language. Go client for Nansen AI API with Smart Money analytics, token screener, profiler, and zero dependencies
How do I install nansen-go?
Install nansen-go with the Go module system using `go get tigusigalpa/nansen-go`. Check the repository for the current installation instructions.
What category does nansen-go belong to?
nansen-go is listed under Third-party APIs, specifically Third-party APIs.