cg is a CoinGecko CLI tool written in Go. It provides both standard CLI commands and an interactive TUI for browsing cryptocurrency market data from the CoinGecko API.
make build # Build binary → ./cg
make test # Run tests with -race
make lint # Run golangci-lint
make clean # Remove binaryOr directly:
go build -o cg .
go test -race ./...coingecko-cli/
├── main.go # Entry point
├── cmd/ # Cobra commands (auth, status, price, markets, search, trending, history, top_gainers_losers, watch, tui, version)
├── internal/
│ ├── api/
│ │ ├── client.go # HTTP client, auth, error handling
│ │ ├── client_test.go # httptest-based tests
│ │ ├── coins.go # API endpoint methods + FetchAllMarkets pagination helper
│ │ ├── coins_test.go # FetchAllMarkets tests
│ │ └── types.go # JSON response structs
│ ├── config/
│ │ ├── config.go # Viper-based config (API key, tier)
│ │ └── config_test.go
│ ├── display/
│ │ ├── banner.go # ASCII logo, welcome box, brand banner
│ │ ├── table.go # Table rendering
│ │ ├── format.go # Price/number formatting
│ │ └── color.go # ANSI color (NO_COLOR/TTY aware)
│ ├── export/
│ │ └── csv.go # CSV file export
│ ├── ws/
│ │ ├── client.go # WebSocket client (ActionCable protocol, reconnect, state machine)
│ │ └── client_test.go # WebSocket client tests (httptest + gorilla/websocket upgrader)
│ └── tui/
│ ├── styles.go # Shared lipgloss styles, brand colors, frame/layout helpers
│ ├── markets.go # Markets TUI model
│ ├── trending.go # Trending TUI model
│ ├── detail.go # Coin detail view
│ └── chart.go # Braille price chart (ntcharts)
├── Makefile
├── .goreleaser.yml
├── install.sh
└── .github/workflows/ # CI (lint, test, build) and Release (goreleaser)
- Go version: 1.26 (pinned in go.mod and CI)
- Binary name:
cg - Module path:
github.com/coingecko/coingecko-cli - Test framework:
testify/assert+net/http/httptest - API client tests: use
httptest.NewServerwithSetBaseURL, never hit real API - Config location:
~/.config/coingecko-cli/config.yaml(dir0700, file0600) - Auth tiers: demo, paid — demo uses
x-cg-demo-api-keyheader andapi.coingecko.com, paid usesx-cg-pro-api-keyandpro-api.coingecko.com - Error model:
ErrInvalidAPIKey(401),ErrPlanRestricted(403),ErrRateLimited(429) — all user-friendly messages - Paid-only endpoints: use
requirePaid()pre-flight check before API call - Output modes: global
-o json/--output jsonflag on all data commands; JSON mode emits raw API structs to stdout - Stream contract: stdout = data only, stderr = diagnostics/warnings (use
warnf()helper incmd/output.go) - Color output: respects
NO_COLORenv var and TTY detection — never hardcode ANSI in output that may be piped - Formatting: all code must be
gofmt-clean - Commits: conventional commit style (
feat:,fix:,chore:) - TUI framework: Bubble Tea (bubbletea) + Lip Gloss (lipgloss) + Bubbles + ntcharts
- Interactive prompts:
huh(Charm ecosystem) — API key input uses password echo mode - Auth key input priority: env var
CG_API_KEY>--keyflag > interactive prompt — env vars are preferred to avoid shell history/process listing exposure
- OAS specs: https://github.com/coingecko/coingecko-api-oas (
coingecko-demo.json,coingecko-pro.json) - API docs: https://docs.coingecko.com
- Rate limiting: 429 responses may include
Retry-After: <seconds>header and/orx-ratelimit-reset: <timestamp>header (format2006-01-02 15:04:05 -0700). In practice, CoinGecko sendsx-ratelimit-resetbut not alwaysRetry-After. The CLI prefersRetry-After, thenx-ratelimit-reset, then local exponential backoff with jitter - Structured errors:
-o jsonmode emits{"error":"rate_limited","message":"...","retry_after":54}to stderr - Dry run:
--dry-runon any data command outputs the API request as JSON without executing - Command catalog:
cg commands(hidden) outputs machine-readable JSON with all commands, flags, OAS operation IDs, and enums
| CLI Command | API Endpoint | OAS Operation ID |
|---|---|---|
cg price |
/simple/price |
simple-price |
cg markets |
/coins/markets |
coins-markets |
cg search |
/search |
search-data |
cg trending |
/search/trending |
trending-search |
cg history --date |
/coins/{id}/history |
coins-id-history |
cg history --days |
/coins/{id}/market_chart |
coins-id-market-chart |
cg history --days --interval hourly |
/coins/{id}/market_chart/range (batched) |
coins-id-market-chart-range |
cg history --days --ohlc |
/coins/{id}/ohlc |
coins-id-ohlc |
cg history --from/--to |
/coins/{id}/market_chart/range |
coins-id-market-chart-range |
cg history --from/--to --interval hourly |
/coins/{id}/market_chart/range (batched) |
coins-id-market-chart-range |
cg history --from/--to --ohlc |
/coins/{id}/ohlc/range (batched for large ranges) |
coins-id-ohlc-range |
cg top-gainers-losers |
/coins/top_gainers_losers |
coins-top-gainers-losers |
cg watch |
wss://stream.coingecko.com/v1 (WebSocket) |
— |
- Homebrew: tap repo at
coingecko/homebrew-coingecko-cli— goreleaser pushes formula on each tagged release - Goreleaser:
.goreleaser.ymlusesbrewsto generate Homebrew formula for CLI binary distribution - Release workflow:
.github/workflows/release.ymltriggers onv*tags, requiresHOMEBREW_TAP_TOKENrepo secret for tap repo write access - Tagging: always tag from
mainafter pulling latest —git tag vX.Y.Z && git push origin vX.Y.Z - Install script:
install.shdownloads the latest release binary from GitHub Releases - Go install:
go install github.com/coingecko/coingecko-cli@latest
- CoinGecko
/coins/{id}/market_chart/rangeexpects UNIX timestamps in seconds — CLI acceptsYYYY-MM-DDand converts in the command layer - CoinGecko
/coins/{id}/historyusesDD-MM-YYYYdate format — CLI acceptsYYYY-MM-DDand converts - Symbol resolution:
cg price --symbolsuses/simple/price?symbols=directly (API accepts symbols natively).cg watch --symbolsuses/searchto resolve symbols to coin IDs (WebSocket requires coin IDs), picking exact case-insensitive match with highest market_cap_rank - TUI detail view fetches coin detail + OHLC concurrently via
tea.Batch RateLimitErrortyped error carriesRetryAfterseconds, satisfieserrors.Is(err, ErrRateLimited)via customIs()method- API text (coin names, symbols) sanitized via
display.SanitizeCellto strip terminal escape injection - History batching: large date ranges are auto-batched into sequential API requests with dedup at chunk boundaries. Batching logic lives in
cmd/history.go(fetchMarketChartBatched,fetchOHLCRangeBatched,dedupTimeseries) - Hourly interval strategy:
--interval hourlyroutes through/market_chart/rangewith nointervalparam, relying on CoinGecko auto-granularity (2–90 day ranges → hourly). Chunks are ≤90 days. Requests for 1 day are padded to 2 days then trimmed - Daily interval on /range: uses auto-granularity (>90 day ranges → daily). Short ranges (<91 days) are padded to 91 days then trimmed to the original range
- OHLC range batching: daily chunks ≤170 days (API limit 180), hourly chunks ≤30 days (API limit 31).
--ohlc --intervalrequires paid plans --intervalvalues: onlydailyandhourlyare accepted;5mis not supported (Enterprise-only)- Hourly data availability: CoinGecko hourly data is only available from 2018-01-30 onwards; 5-minute data from 2018-02-09 onwards. The CLI validates this client-side and rejects requests with
--interval hourlybefore the cutoff date - WebSocket streaming:
cg watchuses CoinGecko's ActionCable WebSocket API (CGSimplePricechannel) for real-time price updates (~10s). Paid-only, USD prices only. API key passed viax_cg_pro_api_keyquery param. Coin IDs are validated via/simple/pricebefore connecting; invalid IDs are skipped with a warning - WebSocket reconnect: automatic reconnect with exponential backoff (1s→30s cap + jitter).
Close()sets an atomicclosingflag that suppresses reconnect. SinglereadLoopgoroutine owns the connection lifecycle - WebSocket test seams:
cmd/client_factory.goexposesStreamerinterface +newStreamerfactory for injecting test doubles in command tests. WS protocol tests usehttptest+gorilla/websocket.Upgrader - Command test seams:
cmd/client_factory.goexposes injectablenewAPIClient,loadConfig, andnewStreamervars so command integration tests can swap in httptest servers and test configs without touching real API or config files - Pagination helper:
FetchAllMarketsininternal/api/coins.gohandles multi-page fetching (250/page) with trim-to-total, used by bothcg marketsandcg tui markets - TUI trending tier awareness: demo gets 15 coins, paid gets 30 via
show_max=coinsAPI param