birdeye

package module
v1.0.1 Latest Latest
Warning

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

Go to latest
Published: Dec 26, 2025 License: MIT Imports: 11 Imported by: 2

README

birdeye-go

CI Go Reference Go Report Card Coverage

A Go client for the Birdeye API - comprehensive DeFi analytics and data for Solana.

Features

  • Token Prices - Real-time prices with decimal.Decimal precision
  • Token Security - Authority checks, holder concentration, Token-2022 detection
  • Token Overview - Market data, liquidity, volume, holder counts
  • Automatic Retries - Exponential backoff for rate limits and server errors
  • Flexible Configuration - Functional options pattern for clean API

Installation

go get github.com/Laminar-Bot/birdeye-go

Quick Start

package main

import (
    "context"
    "fmt"
    "log"

    birdeye "github.com/Laminar-Bot/birdeye-go"
)

func main() {
    // Create client with API key
    client, err := birdeye.NewClient("your-api-key")
    if err != nil {
        log.Fatal(err)
    }

    // Get token price
    price, err := client.GetPrice(context.Background(), "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("BONK: $%s\n", price.Value.String())
}

Configuration Options

import (
    "time"
    birdeye "github.com/Laminar-Bot/birdeye-go"
)

// Configure with options
client, err := birdeye.NewClient("your-api-key",
    birdeye.WithTimeout(30*time.Second),
    birdeye.WithMaxRetries(5),
    birdeye.WithBaseURL("https://custom-proxy.example.com"),
)
Available Options
Option Description Default
WithTimeout(d) HTTP request timeout 10 seconds
WithMaxRetries(n) Maximum retry attempts 3
WithBaseURL(url) Custom API base URL https://public-api.birdeye.so
WithLogger(l) Custom logger implementation No-op logger
WithHTTPClient(c) Custom *http.Client Default with timeout

Token Prices

// Get single token price
price, err := client.GetPrice(ctx, "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("USDC: $%s\n", price.Value.String())
fmt.Printf("24h Change: %s%%\n", price.PriceChange24h.String())

// Get multiple prices (automatically batched for >100 tokens)
prices, err := client.GetMultiplePrices(ctx, []string{
    "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
    "7GCihgDB8fe6KNjn2MYtkzZcRjQy3t9GHdC8uHYmW2hr",
})
for addr, price := range prices {
    fmt.Printf("%s: $%s\n", addr[:8], price.String())
}

Token Security

Check for rug pull indicators:

security, err := client.GetTokenSecurity(ctx, tokenAddress)
if err != nil {
    log.Fatal(err)
}

// Check authority status (active authority = higher risk)
if security.HasMintAuthority() {
    fmt.Println("WARNING: Token has active mint authority")
}
if security.HasFreezeAuthority() {
    fmt.Println("WARNING: Token has active freeze authority")
}

// Check holder concentration
fmt.Printf("Top 10 Holders: %s%%\n", security.Top10HolderPercent)

// Token-2022 specific checks
if security.IsToken2022 && security.TransferFeeEnable {
    fmt.Printf("Transfer Fee: %d bps\n", security.TransferFeeData.TransferFeeBPS)
}

Token Overview

Get comprehensive market data:

overview, err := client.GetTokenOverview(ctx, tokenAddress)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Token: %s (%s)\n", overview.Name, overview.Symbol)
fmt.Printf("Price: $%s\n", overview.Price.String())
fmt.Printf("Liquidity: $%s\n", overview.Liquidity.String())
fmt.Printf("24h Volume: $%s\n", overview.Volume24hUSD.String())
fmt.Printf("Market Cap: $%s\n", overview.MarketCap.String())
fmt.Printf("Holders: %d\n", overview.Holder)

// Social links (if available)
if overview.Extensions != nil {
    if overview.Extensions.Twitter != "" {
        fmt.Printf("Twitter: %s\n", overview.Extensions.Twitter)
    }
}

Error Handling

All API errors are returned as *APIError with helpful methods:

price, err := client.GetPrice(ctx, "invalid-token")
if err != nil {
    if apiErr, ok := birdeye.IsAPIError(err); ok {
        switch {
        case apiErr.IsNotFound():
            fmt.Println("Token not found")
        case apiErr.IsRateLimited():
            fmt.Println("Rate limited - slow down")
        case apiErr.IsServerError():
            fmt.Println("Birdeye server error")
        case apiErr.IsClientError():
            fmt.Printf("Bad request: %s\n", apiErr.Message)
        }
    }
    return
}

Custom Logging

Implement the Logger interface for custom logging:

type Logger interface {
    Debug(msg string, keysAndValues ...interface{})
    Info(msg string, keysAndValues ...interface{})
    Warn(msg string, keysAndValues ...interface{})
    Error(msg string, keysAndValues ...interface{})
}

// Example with zap
type zapAdapter struct{ *zap.SugaredLogger }

func (z *zapAdapter) Debug(msg string, kv ...interface{}) { z.Debugw(msg, kv...) }
func (z *zapAdapter) Info(msg string, kv ...interface{})  { z.Infow(msg, kv...) }
func (z *zapAdapter) Warn(msg string, kv ...interface{})  { z.Warnw(msg, kv...) }
func (z *zapAdapter) Error(msg string, kv ...interface{}) { z.Errorw(msg, kv...) }

client, _ := birdeye.NewClient("api-key",
    birdeye.WithLogger(&zapAdapter{sugar}),
)

Rate Limits

Birdeye enforces API rate limits based on your plan. This client:

  • Automatically retries on 429 (rate limit) responses
  • Uses exponential backoff between retries
  • Respects context cancellation
// Configure retry behavior
client, _ := birdeye.NewClient("api-key",
    birdeye.WithMaxRetries(5),  // Up to 5 retry attempts
)

Financial Precision

All price and amount values use decimal.Decimal from shopspring/decimal to avoid floating-point precision issues:

// Prices are decimal.Decimal, not float64
price := overview.Price                    // decimal.Decimal
liquidity := overview.Liquidity            // decimal.Decimal

// Safe arithmetic
total := price.Mul(decimal.NewFromInt(100))
formatted := price.StringFixed(8)  // "0.00001234"

Contributing

Contributions are welcome! Please read our Contributing Guide first.

License

MIT License - see LICENSE for details.

Documentation

Overview

Package birdeye provides a Go client for the Birdeye DeFi analytics API.

Birdeye (https://birdeye.so) provides comprehensive analytics for Solana tokens including price data, liquidity metrics, holder information, and security analysis.

Quick Start

client, err := birdeye.NewClient("your-api-key")
if err != nil {
    log.Fatal(err)
}

// Get token price
price, err := client.GetPrice(ctx, "So11111111111111111111111111111111111111112")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("SOL: $%s\n", price.Value)

Configuration Options

Use functional options to customize the client:

client, err := birdeye.NewClient("your-api-key",
    birdeye.WithTimeout(30 * time.Second),
    birdeye.WithMaxRetries(5),
    birdeye.WithBaseURL("https://custom-endpoint.example.com"),
)

Error Handling

API errors are returned as *APIError which provides helper methods:

price, err := client.GetPrice(ctx, tokenAddress)
if err != nil {
    if apiErr, ok := birdeye.IsAPIError(err); ok {
        if apiErr.IsRateLimited() {
            // Handle rate limiting
        }
        if apiErr.IsNotFound() {
            // Token not found
        }
    }
    return err
}

Logging

Logging is optional. To enable, provide a Logger implementation:

client, err := birdeye.NewClient("your-api-key",
    birdeye.WithLogger(myLogger),
)

The Logger interface is minimal and can wrap any logging library.

Financial Precision

All monetary values use github.com/shopspring/decimal for precise arithmetic. Never use float64 for financial calculations.

Index

Constants

View Source
const (
	// DefaultBaseURL is the Birdeye public API endpoint.
	DefaultBaseURL = "https://public-api.birdeye.so"

	// DefaultTimeout for HTTP requests.
	DefaultTimeout = 10 * time.Second

	// DefaultMaxRetries before giving up on a request.
	DefaultMaxRetries = 3

	// DefaultRetryWaitMin is the minimum wait time between retries.
	DefaultRetryWaitMin = 500 * time.Millisecond

	// DefaultRetryWaitMax is the maximum wait time between retries.
	DefaultRetryWaitMax = 3 * time.Second
)

API configuration defaults.

Variables

This section is empty.

Functions

This section is empty.

Types

type APIError

type APIError struct {
	// StatusCode is the HTTP status code returned.
	StatusCode int

	// Message is the error message from the API response body.
	Message string

	// Path is the API endpoint that returned the error.
	Path string
}

APIError represents an error response from the Birdeye API.

func IsAPIError

func IsAPIError(err error) (*APIError, bool)

IsAPIError checks if an error is a Birdeye API error and returns it. This correctly handles wrapped errors using errors.As.

func (*APIError) Error

func (e *APIError) Error() string

Error implements the error interface.

func (*APIError) IsClientError

func (e *APIError) IsClientError() bool

IsClientError returns true if the error is a client-side error (4xx).

func (*APIError) IsNotFound

func (e *APIError) IsNotFound() bool

IsNotFound returns true if the error indicates the resource was not found.

func (*APIError) IsRateLimited

func (e *APIError) IsRateLimited() bool

IsRateLimited returns true if the error indicates rate limiting.

func (*APIError) IsServerError

func (e *APIError) IsServerError() bool

IsServerError returns true if the error is a server-side error (5xx).

type Client

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

Client provides methods for interacting with the Birdeye API.

func NewClient

func NewClient(apiKey string, opts ...Option) (*Client, error)

NewClient creates a new Birdeye API client.

The apiKey is required. Additional options can be provided to customize the client behavior.

Example:

client, err := birdeye.NewClient("your-api-key",
    birdeye.WithTimeout(30 * time.Second),
    birdeye.WithMaxRetries(5),
)

func (*Client) GetMultiplePrices

func (c *Client) GetMultiplePrices(ctx context.Context, addresses []string) (map[string]decimal.Decimal, error)

GetMultiplePrices fetches prices for multiple tokens in a single request.

Birdeye supports up to 100 addresses per request. This method handles batching automatically for larger lists.

Returns a map of address -> price. Missing prices are omitted from the result.

Example:

prices, err := client.GetMultiplePrices(ctx, []string{
    "So11111111111111111111111111111111111111112", // SOL
    "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC
})
for addr, price := range prices {
    log.Printf("%s: $%s", addr, price)
}

func (*Client) GetPrice

func (c *Client) GetPrice(ctx context.Context, address string) (*PriceData, error)

GetPrice fetches the current price for a single token.

Example:

price, err := client.GetPrice(ctx, "So11111111111111111111111111111111111111112")
if err != nil {
    return err
}
log.Printf("SOL price: $%s", price.Value)

func (*Client) GetTokenOverview

func (c *Client) GetTokenOverview(ctx context.Context, address string) (*TokenOverview, error)

GetTokenOverview fetches market overview data for a token.

This endpoint provides data for token screening:

  • Liquidity in USD (for minimum liquidity checks)
  • Volume data (activity indicator)
  • Holder count (distribution indicator)
  • Price and market cap

Example:

overview, err := client.GetTokenOverview(ctx, "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")
if err != nil {
    return err
}
if overview.Liquidity.LessThan(decimal.NewFromInt(50000)) {
    log.Warn("liquidity below threshold")
}

func (*Client) GetTokenSecurity

func (c *Client) GetTokenSecurity(ctx context.Context, address string) (*TokenSecurity, error)

GetTokenSecurity fetches security information for a token.

This endpoint provides data for token screening:

  • Mint and freeze authority status
  • Holder concentration (top 10 holders percentage)
  • Creator/owner holdings
  • Token-2022 specific features (transfer fees, etc.)

Example:

security, err := client.GetTokenSecurity(ctx, "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")
if err != nil {
    return err
}
if security.HasMintAuthority() {
    log.Warn("token has active mint authority")
}

type Logger

type Logger interface {
	// Debug logs a debug message with optional key-value pairs.
	Debug(msg string, keysAndValues ...interface{})

	// Info logs an info message with optional key-value pairs.
	Info(msg string, keysAndValues ...interface{})

	// Warn logs a warning message with optional key-value pairs.
	Warn(msg string, keysAndValues ...interface{})

	// Error logs an error message with optional key-value pairs.
	Error(msg string, keysAndValues ...interface{})
}

Logger is an optional interface for structured logging. Implement this to integrate with your logging library.

type Option

type Option func(*config)

Option configures the Client.

func WithBaseURL

func WithBaseURL(url string) Option

WithBaseURL overrides the default API base URL.

func WithHTTPClient

func WithHTTPClient(client *http.Client) Option

WithHTTPClient sets a custom HTTP client. This overrides the default retryable client. Use with caution.

func WithLogger

func WithLogger(l Logger) Option

WithLogger sets a custom logger for the client. If not set, logging is disabled (noop logger is used).

func WithMaxRetries

func WithMaxRetries(n int) Option

WithMaxRetries sets the maximum number of retries for failed requests.

func WithRetryWait

func WithRetryWait(min, max time.Duration) Option

WithRetryWait sets the minimum and maximum wait times between retries.

func WithTimeout

func WithTimeout(d time.Duration) Option

WithTimeout sets the HTTP request timeout.

type PriceData

type PriceData struct {
	// Value is the current price in USD.
	Value decimal.Decimal `json:"value"`

	// UpdateUnixTime is when the price was last updated (Unix timestamp).
	UpdateUnixTime int64 `json:"updateUnixTime"`

	// UpdateHumanTime is a human-readable update timestamp.
	UpdateHumanTime string `json:"updateHumanTime"`

	// PriceChange24h is the 24-hour price change percentage.
	PriceChange24h decimal.Decimal `json:"priceChange24h"`
}

PriceData contains price information for a single token.

type TokenExtensions

type TokenExtensions struct {
	// Coingecko is the CoinGecko listing ID.
	Coingecko string `json:"coingecko,omitempty"`

	// Twitter is the project's Twitter handle or URL.
	Twitter string `json:"twitter,omitempty"`

	// Website is the project's website URL.
	Website string `json:"website,omitempty"`

	// Telegram is the project's Telegram group URL.
	Telegram string `json:"telegram,omitempty"`

	// Discord is the project's Discord server URL.
	Discord string `json:"discord,omitempty"`

	// Description is a brief description of the token.
	Description string `json:"description,omitempty"`
}

TokenExtensions contains optional metadata and social links.

type TokenOverview

type TokenOverview struct {
	// Address is the token's mint address.
	Address string `json:"address"`

	// Symbol is the token's trading symbol (e.g., "SOL").
	Symbol string `json:"symbol"`

	// Name is the token's full name (e.g., "Solana").
	Name string `json:"name"`

	// Decimals is the number of decimal places for the token.
	Decimals int `json:"decimals"`

	// LogoURI is a URL to the token's logo image.
	LogoURI string `json:"logoURI"`

	// Liquidity is the total liquidity in USD across all pools.
	Liquidity decimal.Decimal `json:"liquidity"`

	// Price is the current price in USD.
	Price decimal.Decimal `json:"price"`

	// PriceChange24hPercent is the 24-hour price change percentage.
	PriceChange24hPercent decimal.Decimal `json:"priceChange24hPercent"`

	// Volume24h is the 24-hour trading volume in the token's native units.
	Volume24h decimal.Decimal `json:"v24h"`

	// Volume24hUSD is the 24-hour trading volume in USD.
	Volume24hUSD decimal.Decimal `json:"v24hUSD"`

	// Volume24hChangePercent is the change in volume vs previous 24h.
	Volume24hChangePercent decimal.Decimal `json:"v24hChangePercent"`

	// MarketCap is the market capitalization in USD.
	MarketCap decimal.Decimal `json:"mc"`

	// Supply is the total token supply.
	Supply decimal.Decimal `json:"supply"`

	// CirculatingSupply is the supply in circulation.
	CirculatingSupply decimal.Decimal `json:"circulatingSupply"`

	// Holder is the number of unique token holders.
	Holder int `json:"holder"`

	// Trade24h is the number of trades in the last 24 hours.
	Trade24h int `json:"trade24h"`

	// Trade24hChangePercent is the change in trade count vs previous 24h.
	Trade24hChangePercent decimal.Decimal `json:"trade24hChangePercent"`

	// Buy24h is the number of buy trades in the last 24 hours.
	Buy24h int `json:"buy24h"`

	// Sell24h is the number of sell trades in the last 24 hours.
	Sell24h int `json:"sell24h"`

	// UniqueWallet24h is the number of unique wallets trading in 24h.
	UniqueWallet24h int `json:"uniqueWallet24h"`

	// UniqueWallet24hChangePercent is the change vs previous 24h.
	UniqueWallet24hChangePercent decimal.Decimal `json:"uniqueWallet24hChangePercent"`

	// LastTradeUnixTime is the Unix timestamp of the last trade.
	LastTradeUnixTime int64 `json:"lastTradeUnixTime"`

	// LastTradeHumanTime is a human-readable timestamp of the last trade.
	LastTradeHumanTime string `json:"lastTradeHumanTime"`

	// Extensions contains optional metadata links.
	Extensions *TokenExtensions `json:"extensions,omitempty"`
}

TokenOverview contains market and metadata information about a token.

This data is used to check:

  • Liquidity (minimum threshold for trading)
  • Trading volume (activity indicator)
  • Market cap and holder count

type TokenSecurity

type TokenSecurity struct {
	// MintAuthority is the address that can mint new tokens.
	// Nil or empty string means no mint authority (safer).
	MintAuthority *string `json:"mintAuthority"`

	// FreezeAuthority is the address that can freeze token accounts.
	// Nil or empty string means no freeze authority (safer).
	FreezeAuthority *string `json:"freezeAuthority"`

	// CreatorAddress is the token creator's wallet address.
	CreatorAddress string `json:"creatorAddress"`

	// CreatorBalance is the creator's current token balance.
	CreatorBalance string `json:"creatorBalance"`

	// CreatorPercentage is the percentage of supply held by creator.
	CreatorPercentage string `json:"creatorPercentage"`

	// OwnerAddress is the current owner of the token mint.
	OwnerAddress string `json:"ownerAddress"`

	// OwnerBalance is the owner's current token balance.
	OwnerBalance string `json:"ownerBalance"`

	// OwnerPercentage is the percentage of supply held by owner.
	OwnerPercentage string `json:"ownerPercentage"`

	// Top10HolderBalance is the combined balance of top 10 holders.
	Top10HolderBalance string `json:"top10HolderBalance"`

	// Top10HolderPercent is the percentage of supply held by top 10 holders.
	Top10HolderPercent string `json:"top10HolderPercent"`

	// Top10UserBalance is the combined balance of top 10 non-contract holders.
	Top10UserBalance string `json:"top10UserBalance"`

	// Top10UserPercent is the percentage of supply held by top 10 users.
	Top10UserPercent string `json:"top10UserPercent"`

	// TotalSupply is the total token supply.
	TotalSupply string `json:"totalSupply"`

	// IsToken2022 indicates if this is a Token-2022 (new token program) token.
	IsToken2022 bool `json:"isToken2022"`

	// TransferFeeEnable indicates if transfer fees are enabled (Token-2022).
	TransferFeeEnable bool `json:"transferFeeEnable"`

	// TransferFeeData contains fee configuration if enabled.
	TransferFeeData *TransferFeeData `json:"transferFeeData,omitempty"`

	// NonTransferable indicates if the token is non-transferable (soulbound).
	NonTransferable bool `json:"nonTransferable"`

	// MutableMetadata indicates if token metadata can be changed.
	MutableMetadata bool `json:"mutableMetadata"`
}

TokenSecurity contains security-related information about a token.

This data is used to check for red flags like:

  • Active mint authority (can create more tokens)
  • Active freeze authority (can freeze user accounts)
  • High holder concentration (rug pull risk)

func (*TokenSecurity) HasFreezeAuthority

func (ts *TokenSecurity) HasFreezeAuthority() bool

HasFreezeAuthority returns true if the token has an active freeze authority.

Tokens with freeze authority are higher risk because user accounts can be frozen, preventing transfers or sales.

func (*TokenSecurity) HasMintAuthority

func (ts *TokenSecurity) HasMintAuthority() bool

HasMintAuthority returns true if the token has an active mint authority.

Tokens with mint authority are higher risk because more tokens can be minted at any time, diluting existing holders.

type TransferFeeData

type TransferFeeData struct {
	// TransferFeeBPS is the fee in basis points (100 = 1%).
	TransferFeeBPS int `json:"transferFeeBps"`

	// MaxFee is the maximum fee amount.
	MaxFee string `json:"maxFee"`

	// FeeAuthority can update the fee configuration.
	FeeAuthority string `json:"feeAuthority"`

	// WithdrawAuthority can withdraw collected fees.
	WithdrawAuthority string `json:"withdrawAuthority"`
}

TransferFeeData contains Token-2022 transfer fee configuration.

Jump to

Keyboard shortcuts

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