whalealert

package module
v1.0.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 29, 2026 License: MIT Imports: 10 Imported by: 0

README

Whale Alert Golang SDK

Whale Alert Golang SDK

Go Reference CI CodeQL Codecov Go Report Card MIT License

An unofficial Go client library for the Whale Alert Enterprise API.

Disclaimer: This is an unofficial SDK and is not affiliated with, endorsed by, or sponsored by Whale Alert. All product names, logos, and brands are property of their respective owners.

What is this?

This library makes it easy to talk to the Whale Alert Enterprise API from Go. Whether you need to check which blockchains are supported, query transactions and blocks, or listen to real-time whale movements over WebSocket, the client gives you typed, idiomatic Go methods with sane defaults and robust error handling.

It is built around a few ideas that should feel familiar to Go developers:

  • Everything accepts context.Context for timeouts and cancellation.
  • The client is safe to share across goroutines.
  • Monetary values stay as strings so you never lose precision.
  • Retries, pagination, and connection recovery are opt-in but easy to enable.

Features

  • REST API: Full coverage of the documented endpoints — status, blockchain status, transactions, blocks, and address transactions.
  • WebSocket API: Real-time alerts and socials with subscription management, automatic reconnection, ping/pong keep-alive, and event decoding.
  • Typed models: Strongly-typed structs for every API response, so your editor can help you explore the data.
  • Financial precision: Amounts and fees are kept as string to avoid the rounding issues that come from float64.
  • Retry policy: Configurable exponential backoff with jitter for idempotent GET requests when the API returns 429 or 5xx errors.
  • Pagination: Typed page objects with a lazy iterator and safe next-URL following. The library validates next URLs against the configured base origin so you cannot accidentally follow a malicious link.
  • Error handling: A typed APIError plus sentinel errors (ErrUnauthorized, ErrRateLimited, and others) that work with errors.Is and errors.As.
  • Context support: Every REST call accepts context.Context for deadlines, cancellation, and request-scoped values.
  • Concurrency safe: Create one client and reuse it across multiple goroutines without extra synchronization.
  • API key security: Your API key is never logged. Request hooks receive URLs with the key redacted.

Installation

Add the module to your project with go get:

go get github.com/tigusigalpa/whale-alert-go

Then import the package in your Go files:

import whalealert "github.com/tigusigalpa/whale-alert-go"

Quick Start

REST API

This example shows how to create a client, call a public endpoint that does not require an API key, and then call an authenticated endpoint that does:

package main

import (
    "context"
    "fmt"
    "log"
    "os"
    "time"

    whalealert "github.com/tigusigalpa/whale-alert-go"
)

func main() {
    // Read the API key from the environment. Keep secrets out of source code.
    apiKey := os.Getenv("WHALE_ALERT_API_KEY")

    // Create a client with retries enabled: up to 3 attempts, starting at
    // 500ms and capped at 10s. Retries only apply to idempotent GET requests.
    client := whalealert.NewClient(apiKey,
        whalealert.WithRetry(3, 500*time.Millisecond, 10*time.Second),
    )

    // Public endpoint — no API key required.
    // Returns the list of blockchains Whale Alert supports.
    chains, err := client.Status.GetSupportedBlockchains(context.Background())
    if err != nil {
        log.Fatal(err)
    }
    for _, c := range chains {
        fmt.Printf("%s: %v\n", c.Name, c.Symbols)
    }

    // Authenticated endpoint — requires a valid API key.
    // Returns the current sync status for Ethereum.
    status, err := client.Status.GetBlockchainStatus(context.Background(), "ethereum")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Ethereum: %d-%d (%d blocks)\n", status.StartHeight, status.EndHeight, status.BlockCount)
}
WebSocket API

The WebSocket client streams real-time alerts. You register message and error handlers, connect, subscribe, and then call Listen to enter the read loop. Automatic reconnection is opt-in: set MaxAttempts greater than zero.

package main

import (
    "context"
    "fmt"
    "log"
    "os"
    "time"

    "github.com/tigusigalpa/whale-alert-go/websocket"
)

func main() {
    apiKey := os.Getenv("WHALE_ALERT_API_KEY")
    wsURL := fmt.Sprintf("wss://leviathan.whale-alert.io/ws?api_key=%s", apiKey)

    // Configure the WebSocket client. Reconnection starts at 1 second
    // and doubles each attempt up to the 30 second cap.
    client := websocket.NewClient(websocket.Config{
        URL: wsURL,
        Reconnect: websocket.ReconnectConfig{
            MaxAttempts:  5,
            InitialDelay: 1 * time.Second,
            MaxDelay:     30 * time.Second,
        },
    })

    // Called for every decoded message.
    client.OnMessage(func(msg websocket.Message) {
        if msg.EventType == websocket.EventTypeAlert && msg.Alert != nil {
            fmt.Printf("[ALERT] %s: %s\n", msg.Alert.Blockchain, msg.Alert.Text)
        }
    })

    // Called when a non-fatal error happens, such as a temporary disconnect.
    client.OnError(func(err error) {
        log.Printf("Error: %v", err)
    })

    ctx := context.Background()
    if err := client.Connect(ctx); err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    // Subscribe to Ethereum whale alerts worth at least $500,000.
    if err := client.SubscribeAlerts(ctx, websocket.AlertSubscription{
        ID:           "my-sub",
        Blockchains:  []string{"ethereum"},
        MinValueUSD:  500000,
    }); err != nil {
        log.Fatal(err)
    }

    // Listen blocks until the connection closes or the context is cancelled.
    if err := client.Listen(ctx); err != nil {
        log.Fatal(err)
    }
}

Configuration

Client Options

You can customize the REST client through functional options passed to NewClient:

Option Description Default
WithBaseURL(url) Override the API base URL https://leviathan.whale-alert.io
WithHTTPClient(hc) Use a custom *http.Client Default client with a 30s timeout
WithTimeout(d) Set the HTTP client timeout 30s
WithUserAgent(ua) Override the User-Agent header whale-alert-go/1.0.0
WithRetry(n, init, max) Enable retries with exponential backoff 0 retries (disabled)
WithRequestHook(h) Add a hook called before each request none
Retry Policy

Retries are disabled by default. Enable them with WithRetry:

client := whalealert.NewClient(apiKey,
    whalealert.WithRetry(3, 500*time.Millisecond, 10*time.Second),
)

The retry policy is designed to be safe and predictable:

  • Only idempotent GET requests are retried. State-changing operations are never retried automatically.
  • Retries happen on HTTP 429 (rate limited) and 5xx server errors.
  • The 429 response can include a Retry-After header. When present, the client waits at least that long before the next attempt.
  • Backoff is exponential: initialDelay * 2^attempt, capped at maxDelay.
  • Context cancellation is respected during the backoff sleep, so a cancelled request stops immediately.

Error Handling

Every API error is wrapped in an APIError value. You can inspect the HTTP status code and message, or use sentinel errors for common cases.

status, err := client.Status.GetBlockchainStatus(ctx, "ethereum")
if err != nil {
    var apiErr *whalealert.APIError
    if errors.As(err, &apiErr) {
        fmt.Printf("Status: %d, Message: %s\n", apiErr.StatusCode, apiErr.Message)
    }

    if errors.Is(err, whalealert.ErrUnauthorized) {
        // Handle invalid API key — check that WHALE_ALERT_API_KEY is set correctly.
    }
    if errors.Is(err, whalealert.ErrRateLimited) {
        // Handle rate limiting — you may want to back off or inspect apiErr.RetryAfter.
    }
}
Sentinel Errors
Error HTTP Status Typical cause
ErrBadRequest 400 Malformed request parameters
ErrUnauthorized 401 Missing or invalid API key
ErrForbidden 403 Insufficient permissions
ErrNotFound 404 Unknown blockchain, transaction, or block
ErrValidation 422 Parameter validation failure
ErrRateLimited 429 Too many requests
ErrProviderAPI Any 4xx/5xx Catch-all for other provider errors

Pagination

List endpoints return a TransactionPage that includes the current slice of transactions and an optional Next URL. You have two ways to move through pages.

Lazy iterator

The iterator handles page fetching for you, stopping when there are no more results or when an error occurs:

page, err := client.Transactions.ListTransactions(ctx, "ethereum", whalealert.TransactionOptions{
    StartHeight: 768801,
    Limit:       100,
})
if err != nil {
    log.Fatal(err)
}

iter := whalealert.NewTransactionIterator(ctx, client, page)
for iter.HasNext() {
    tx, err := iter.Next()
    if err != nil {
        log.Printf("pagination error: %v", err)
        break
    }
    fmt.Printf("tx: %s\n", tx.Hash)
}
Manual next-page fetching

If you prefer to control pagination yourself, use the Next URL directly. The client validates that the URL shares the same origin as the configured base URL before sending the request.

if page.Next != "" {
    nextPage, err := client.Transactions.ListTransactionsNext(ctx, page.Next)
    if err != nil {
        log.Fatal(err)
    }
    // process nextPage...
}

Address transaction pagination is available through GetAddressTransactionsNext.

Financial Precision

Cryptocurrency amounts can be very small or very large, and float64 cannot represent them exactly. For that reason, all monetary fields in this library (fee, amount in addresses, and similar) are kept as string.

Keep them as strings for display or pass them to a decimal-arithmetic package such as shopspring/decimal. Only convert to float64 if you fully understand the precision implications.

API Reference

The client is organized into services that mirror the API endpoints.

  • Status
    • GetSupportedBlockchains()GET /status (public, no key needed)
    • GetBlockchainStatus(blockchain)GET /{blockchain}/status
  • Transactions
    • GetTransaction(blockchain, hash)GET /{blockchain}/transaction/{hash}
    • ListTransactions(blockchain, opts)GET /{blockchain}/transactions
    • ListTransactionsNext(nextURL) — Follow a pagination URL returned by a previous list call
  • Blocks
    • GetBlock(blockchain, height)GET /{blockchain}/block/{height}
  • Addresses
    • GetAddressTransactions(blockchain, address, opts)GET /{blockchain}/address/{hash}/transactions
    • GetAddressTransactionsNext(nextURL) — Follow a pagination URL for address transactions

For the official API documentation, visit https://developer.whale-alert.io/api-account/documentation.

Examples

Runnable examples are in the examples/ directory:

  • examples/rest/ — REST API usage
  • examples/websocket/ — WebSocket alerts subscription

Run them from the repository root:

WHALE_ALERT_API_KEY=your-key go run examples/rest/main.go
WHALE_ALERT_API_KEY=your-key go run examples/websocket/main.go

Testing

The project includes unit tests for the REST client, WebSocket client, pagination helpers, and error handling. Run the full suite with:

go test ./...
go test -race ./...

You can also run the standard Go quality checks:

go vet ./...
gofmt -l .

License

MIT — see LICENSE

Author

Igor Sazonov — github.com/tigusigalpa

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrUnauthorized  = errors.New("whalealert: unauthorized")
	ErrForbidden     = errors.New("whalealert: forbidden")
	ErrNotFound      = errors.New("whalealert: not found")
	ErrRateLimited   = errors.New("whalealert: rate limited")
	ErrBadRequest    = errors.New("whalealert: bad request")
	ErrValidation    = errors.New("whalealert: validation error")
	ErrTransport     = errors.New("whalealert: transport error")
	ErrDecoding      = errors.New("whalealert: decoding error")
	ErrProviderAPI   = errors.New("whalealert: provider API error")
	ErrMissingAPIKey = errors.New("whalealert: API key required for this endpoint")
)

Sentinel errors for use with errors.Is.

Functions

This section is empty.

Types

type APIError

type APIError struct {
	StatusCode  int
	Message     string
	BodyExcerpt string
	Headers     http.Header
	RetryAfter  *time.Duration
}

APIError represents an error returned by the Whale Alert API.

func (*APIError) As

func (e *APIError) As(target interface{}) bool

As assigns this API error to a compatible target.

func (*APIError) Error

func (e *APIError) Error() string

func (*APIError) Is

func (e *APIError) Is(target error) bool

Is reports whether the API error corresponds to a sentinel API error.

type Address

type Address struct {
	Amount  string `json:"amount"`
	Address string `json:"address"`
	Owner   string `json:"owner,omitempty"`
}

Address represents an input or output address in a sub-transaction. Amount is kept as a string to preserve provider-provided precision.

type AddressTransactionOptions

type AddressTransactionOptions struct {
	Symbol          string // Optional: filter by symbol
	TransactionType string // Optional: filter by transaction type
	Limit           int    // Optional: max results per page
	StartIndex      int    // Optional: pagination offset within the page
	Order           string // Optional: "asc" or "desc"
}

AddressTransactionOptions controls the query parameters for listing address transactions.

type AddressTransactionPage

type AddressTransactionPage struct {
	Transactions []Transaction `json:"transactions"`
	Next         string        `json:"next"`
}

AddressTransactionPage represents a page of address transactions with a next URL.

type AddressesService

type AddressesService struct {
	// contains filtered or unexported fields
}

AddressesService provides access to address-related endpoints.

func (*AddressesService) GetAddressTransactions

func (s *AddressesService) GetAddressTransactions(ctx context.Context, blockchain, address string, opts AddressTransactionOptions) (*AddressTransactionPage, error)

GetAddressTransactions returns transactions for an address from the last 30 days.

GET /{blockchain}/address/{hash}/transactions https://developer.whale-alert.io/api-account/documentation#v2-address

func (*AddressesService) GetAddressTransactionsNext

func (s *AddressesService) GetAddressTransactionsNext(ctx context.Context, nextURL string) (*AddressTransactionPage, error)

GetAddressTransactionsNext fetches the next page of address transactions using the provider-supplied next URL.

type Block

type Block struct {
	Timestamp    int64         `json:"timestamp"`
	Hash         string        `json:"hash"`
	Transactions []Transaction `json:"transactions"`
}

Block represents a block at a specific height.

type BlockPage

type BlockPage struct {
	Timestamp    int64         `json:"timestamp"`
	Hash         string        `json:"hash"`
	Transactions []Transaction `json:"transactions"`
	Next         string        `json:"next"`
}

BlockPage represents a block response that includes transactions and a next URL.

type Blockchain

type Blockchain struct {
	Name    string   `json:"name"`
	Symbols []string `json:"symbols"`
}

Blockchain represents a supported blockchain and its symbols.

type BlockchainStatus

type BlockchainStatus struct {
	StartHeight int64 `json:"start_height"`
	EndHeight   int64 `json:"end_height"`
	BlockCount  int64 `json:"block_count"`
}

BlockchainStatus represents the availability window of a blockchain.

type BlocksService

type BlocksService struct {
	// contains filtered or unexported fields
}

BlocksService provides access to block-related endpoints.

func (*BlocksService) GetBlock

func (s *BlocksService) GetBlock(ctx context.Context, blockchain string, height int64) (*Block, error)

GetBlock returns a block at a specific height.

GET /{blockchain}/block/{height} https://developer.whale-alert.io/api-account/documentation#v2-block

type Client

type Client struct {
	Status       *StatusService
	Transactions *TransactionsService
	Blocks       *BlocksService
	Addresses    *AddressesService
	// contains filtered or unexported fields
}

Client is the Whale Alert API HTTP client. It is safe for concurrent use by multiple goroutines. Do not mutate fields after construction.

func NewClient

func NewClient(apiKey string, opts ...ClientOption) *Client

NewClient creates a new Whale Alert API client. The apiKey is required for authenticated endpoints; the public GET /status endpoint works without it.

type ClientOption

type ClientOption func(*Client)

ClientOption configures a Client.

func WithBaseURL

func WithBaseURL(u string) ClientOption

WithBaseURL overrides the default production base URL.

func WithHTTPClient

func WithHTTPClient(hc *http.Client) ClientOption

WithHTTPClient replaces the default HTTP client.

func WithRequestHook

func WithRequestHook(hook RequestHook) ClientOption

WithRequestHook adds a hook invoked before each HTTP request. Multiple hooks are called in registration order.

func WithRetry

func WithRetry(maxAttempts int, initialDelay, maxDelay time.Duration) ClientOption

WithRetry configures the retry policy for idempotent GET requests. Set maxAttempts to 0 to disable retries (the default).

func WithTimeout

func WithTimeout(d time.Duration) ClientOption

WithTimeout sets the HTTP client timeout.

func WithUserAgent

func WithUserAgent(ua string) ClientOption

WithUserAgent overrides the default User-Agent header.

type RequestHook

type RequestHook func(ctx context.Context, method, redactedURL string, body io.Reader)

RequestHook is called before each HTTP request is sent. The URL passed to the hook has the api_key query parameter redacted.

type RetryConfig

type RetryConfig struct {
	// MaxAttempts is the maximum number of retry attempts (0 = no retries).
	MaxAttempts int
	// InitialDelay is the delay before the first retry.
	InitialDelay time.Duration
	// MaxDelay caps the exponential backoff delay.
	MaxDelay time.Duration
}

RetryConfig controls the retry behavior for idempotent GET requests.

type StatusService

type StatusService struct {
	// contains filtered or unexported fields
}

StatusService provides access to status-related endpoints.

func (*StatusService) GetBlockchainStatus

func (s *StatusService) GetBlockchainStatus(ctx context.Context, blockchain string) (*BlockchainStatus, error)

GetBlockchainStatus returns the availability window for a specific blockchain. An API key is required.

GET /{blockchain}/status https://developer.whale-alert.io/api-account/documentation#v2-blockchainstatus

func (*StatusService) GetSupportedBlockchains

func (s *StatusService) GetSupportedBlockchains(ctx context.Context) ([]Blockchain, error)

GetSupportedBlockchains returns the list of supported blockchains and their symbols. This endpoint does not require an API key.

GET /status https://developer.whale-alert.io/api-account/documentation#v2-blockchains

type SubTransaction

type SubTransaction struct {
	Symbol          string    `json:"symbol"`
	TransactionType string    `json:"transaction_type"`
	Inputs          []Address `json:"inputs"`
	Outputs         []Address `json:"outputs"`
}

SubTransaction represents a single currency/type split within a transaction.

type Transaction

type Transaction struct {
	Height          int64            `json:"height"`
	IndexInBlock    int64            `json:"index_in_block"`
	Timestamp       int64            `json:"timestamp"`
	Hash            string           `json:"hash"`
	Fee             string           `json:"fee"`
	FeeSymbol       string           `json:"fee_symbol"`
	FeeSymbolPrice  json.Number      `json:"fee_symbol_price"`
	SubTransactions []SubTransaction `json:"sub_transactions"`
}

Transaction represents a normalized blockchain transaction. Fee and fee_symbol_price may be strings or numbers from the provider; fee is always kept as a string to preserve precision.

type TransactionIterator

type TransactionIterator struct {
	// contains filtered or unexported fields
}

TransactionIterator provides lazy iteration over paginated transaction results. It fetches the next page only when the consumer advances it. Do not retain every page in memory; process items as they arrive.

func NewTransactionIterator

func NewTransactionIterator(ctx context.Context, client *Client, page *TransactionPage) *TransactionIterator

NewTransactionIterator creates an iterator from an initial page.

func (*TransactionIterator) HasNext

func (it *TransactionIterator) HasNext() bool

HasNext returns true if there are more transactions to iterate, either in the current page or via the next URL.

func (*TransactionIterator) Next

func (it *TransactionIterator) Next() (*Transaction, error)

Next advances the iterator and returns the next transaction. It returns io.EOF when all pages are exhausted. It fetches the next page lazily.

type TransactionOptions

type TransactionOptions struct {
	StartHeight     int64  // Required: starting block height
	Symbol          string // Optional: filter by symbol (e.g. "BTC")
	TransactionType string // Optional: filter by transaction type (e.g. "transfer")
	Limit           int    // Optional: max results per page
	StartIndex      int    // Optional: pagination offset within the page
	Order           string // Optional: "asc" or "desc"
	Format          string // Optional: response format
}

TransactionOptions controls the query parameters for listing transactions.

type TransactionPage

type TransactionPage struct {
	Transactions []Transaction `json:"transactions"`
	Next         string        `json:"next"`
}

TransactionPage represents a page of transactions with a next URL.

type TransactionsService

type TransactionsService struct {
	// contains filtered or unexported fields
}

TransactionsService provides access to transaction-related endpoints.

func (*TransactionsService) GetTransaction

func (s *TransactionsService) GetTransaction(ctx context.Context, blockchain, hash string) (*Transaction, error)

GetTransaction returns a single transaction by its hash.

GET /{blockchain}/transaction/{hash} https://developer.whale-alert.io/api-account/documentation#v2-transaction

func (*TransactionsService) ListTransactions

func (s *TransactionsService) ListTransactions(ctx context.Context, blockchain string, opts TransactionOptions) (*TransactionPage, error)

ListTransactions returns a page of transactions starting at the given height.

GET /{blockchain}/transactions https://developer.whale-alert.io/api-account/documentation#v2-transactions

func (*TransactionsService) ListTransactionsNext

func (s *TransactionsService) ListTransactionsNext(ctx context.Context, nextURL string) (*TransactionPage, error)

ListTransactionsNext fetches the next page of transactions using the provider-supplied next URL. The URL is validated against the configured base URL to prevent following unsafe external URLs.

Directories

Path Synopsis
examples
rest command
websocket command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL