A high-performance, clean-architecture REST API built with Go and Gin framework for accessing cryptocurrency exchange data through the CCXT (CryptoCurrency eXchange Trading) library. Designed for real-time market data retrieval, symbol management, and OHLCV (Open, High, Low, Close, Volume) candle data across multiple crypto exchanges.
- Multi-Exchange Support: Connect to 100+ cryptocurrency exchanges via CCXT
- Smart Caching: Built-in caching layer with configurable TTL to reduce API calls
- Rate Limiting: Request rate limiting to prevent service overload
- OHLCV Data: Historical candlestick data with parallel fetch optimization
- Health Checks: Kubernetes-ready health check endpoints
- CORS Support: Configurable cross-origin request handling
- Compression: Automatic response compression for bandwidth optimization
- Structured Logging: Zap-based JSON/console logging with configurable levels
- Error Handling: Centralized error handling with appropriate HTTP status codes
- Request Tracking: Unique request IDs for request tracing
ChartWise follows a strict clean architecture pattern with clear layer separation:
HTTP Request
↓
Middleware Pipeline (Recovery, Logger, CORS, Rate Limit, Timeout, etc.)
↓
Router (Request routing and parameter validation)
↓
Handler Layer (Parse request, validate input, call services)
↓
Service Layer (Business logic, caching, data transformation)
↓
External APIs (CCXT for exchange data)
chartwise-api-go/
├── cmd/
│ ├── api/
│ │ └── main.go # Application entry point
│ └── bench/
│ └── main.go # Benchmarking tools
├── internal/
│ ├── config/
│ │ └── config.go # Configuration management (Viper)
│ ├── dto/
│ │ ├── common.go # Common DTOs
│ │ └── exchange.go # Exchange-specific DTOs
│ ├── handler/
│ │ ├── health.go # Health check handlers
│ │ └── exchange.go # Exchange endpoint handlers
│ ├── middleware/
│ │ ├── recovery.go # Panic recovery
│ │ ├── request_id.go # Request ID generation
│ │ ├── logger.go # Structured logging
│ │ ├── cors.go # CORS handling
│ │ ├── compression.go # Response compression
│ │ ├── response.go # Response wrapping
│ │ ├── timeout.go # Request timeout
│ │ ├── ratelimit.go # Rate limiting
│ │ └── validator.go # Input validation
│ ├── router/
│ │ └── router.go # Route registration
│ └── services/
│ ├── health_service.go # Health check logic
│ ├── exchange_service.go # Exchange operations
│ └── exchange_service_parallel.go # Parallel data fetching
├── pkg/
│ ├── errors/
│ │ └── errors.go # Centralized error types
│ ├── logger/
│ │ └── logger.go # Zap-based logger wrapper
│ └── utils/
│ ├── string.go # String utilities
│ ├── map.go # Map utilities
│ └── validator.go # Validation utilities
├── go.mod # Module definition
├── go.sum # Dependency checksums
└── .env.example # Example environment configuration
- Go 1.25.5 or higher
- Clone the repository:
git clone https://github.com/sukruozdemir/chartwise-api-go.git
cd chartwise-api-go- Install dependencies:
go mod download- Create environment configuration:
cp .env.example .env- Build the application:
go build -o bin/api ./cmd/api/go run ./cmd/api/main.goThe server will start on http://localhost:8080 (configurable via APP_PORT).
go build -o bin/api ./cmd/api/
./bin/apiLiveness probe endpoint (Kubernetes standard)
curl http://localhost:8080/healthzReadiness probe endpoint (Kubernetes standard)
curl http://localhost:8080/readyzSimple connectivity test
curl http://localhost:8080/api/v1/pingResponse:
{
"success": true,
"message": "pong",
"data": {
"version": "1.0.0"
}
}API information and metadata
curl http://localhost:8080/api/v1/infoList all available exchanges
curl http://localhost:8080/api/v1/exchange/availableResponse:
{
"success": true,
"data": {
"exchanges": [
{
"id": "binance",
"name": "binance",
"country": "JP",
"url": "https://www.binance.com"
},
...
],
"total": 87
}
}Get trading symbols for a specific exchange
Query Parameters:
exchange(required): Exchange name (e.g., "binance", "kraken")type(optional): Filter by type - "spot" or "swap"short(optional): Return minimal data (true/false), default false
# Get all symbols
curl "http://localhost:8080/api/v1/exchange/symbols?exchange=binance"
# Get only spot trading pairs
curl "http://localhost:8080/api/v1/exchange/symbols?exchange=binance&type=spot"
# Get compact symbol list
curl "http://localhost:8080/api/v1/exchange/symbols?exchange=binance&short=true"Response:
{
"success": true,
"data": {
"exchange": "binance",
"spot": [
{
"id": "BTC/USDT",
"symbol": "BTC/USDT",
"base": "BTC",
"quote": "USDT",
"type": "spot",
"active": true
},
...
],
"swap": [...]
}
}Get historical OHLCV candle data
Query Parameters:
exchange(required): Exchange namesymbol(required): Trading pair (e.g., "BTC/USDT")timeframe(required): Candle timeframe ("1m", "5m", "15m", "1h", "4h", "1d", etc.)limit(optional): Number of candles to fetch, default 1000
curl "http://localhost:8080/api/v1/exchange/ohlcv?exchange=binance&symbol=BTC/USDT&timeframe=1h&limit=100"Configuration is managed through environment variables. Create a .env file in the project root:
# Application
APP_NAME=chartwise-api
APP_ENV=development
APP_PORT=8080
APP_VERSION=1.0.0
# Logging
LOG_LEVEL=info # debug, info, warn, error, fatal
LOG_FORMAT=json # json or console
# Server
SERVER_READ_TIMEOUT=15s
SERVER_WRITE_TIMEOUT=15s
SERVER_IDLE_TIMEOUT=60s
SERVER_SHUTDOWN_TIMEOUT=10s
SERVER_MAX_HEADER_BYTES=1048576
SERVER_ENABLE_COMPRESSION=true
# CORS
CORS_ALLOWED_ORIGINS=*
CORS_ALLOWED_METHODS=GET,POST,PUT,DELETE,PATCH,OPTIONS
CORS_ALLOWED_HEADERS=Content-Type,Authorization,X-Requested-With
CORS_ALLOW_CREDENTIALS=true
CORS_MAX_AGE=3600
# Rate Limiting
RATE_LIMIT_ENABLED=true
RATE_LIMIT_RPS=100 # Requests per second per IP
RATE_LIMIT_BURST=200 # Burst size
# Caching
CACHE_ENABLED=true
CACHE_TTL=15m # Time to live for cached data
CACHE_MAX_SIZE=100
CACHE_MAX_INSTANCES=10
# OHLCV Data Fetching
OHLCV_DEFAULT_LIMIT=1000
OHLCV_RATE_LIMIT_BUFFER=0.5 # Buffer in seconds between requests
OHLCV_MAX_WORKERS=5 # Parallel workers for data fetching- Define DTOs in
internal/dto/for request/response structures:
type MyRequest struct {
Param string `json:"param" binding:"required"`
}
type MyResponse struct {
Data string `json:"data"`
}- Create Service in
internal/services/with business logic:
type MyService interface {
DoSomething(ctx context.Context, req *dto.MyRequest) (*dto.MyResponse, error)
}
type myService struct {
logger *logger.Logger
config *config.Config
}
func NewMyService(log *logger.Logger, cfg *config.Config) MyService {
return &myService{logger: log, config: cfg}
}- Create Handler in
internal/handler/to expose HTTP endpoints:
type MyHandler struct {
logger *logger.Logger
service services.MyService
}
func (h *MyHandler) HandleRequest(c *gin.Context) {
// Validate input, call service, return response
}
func (h *MyHandler) RegisterRoutes(rg *gin.RouterGroup) {
rg.POST("/my-endpoint", h.HandleRequest)
}- Register in Router in
internal/router/router.go:
myService := services.NewMyService(appLogger, cfg)
myHandler := handler.NewMyHandler(appLogger, myService)
myHandler.RegisterRoutes(v1)Use centralized error handling from pkg/errors/:
// Return specific HTTP status codes
if err != nil {
return errors.BadRequest("invalid parameter")
return errors.NotFound("resource")
return errors.InternalServer("unexpected error")
return errors.ServiceUnavailable("external service down")
}Use structured logging with key-value pairs:
logger.Info("Operation completed", "user_id", 123, "duration_ms", 45)
logger.Error("Operation failed", "error", err, "retry_count", 3)go test ./...go test ./... -covergo run ./cmd/bench/main.go- Caching: Exchange symbols are cached with configurable TTL to minimize repeated API calls
- Parallel Fetching: OHLCV data fetching uses parallel workers to improve throughput
- Rate Limiting: Configurable per-IP rate limiting prevents service abuse
- Compression: Automatic response compression reduces bandwidth usage
- Connection Pooling: HTTP client connection reuse via Gin's built-in pooling
- Set
APP_ENV=production - Set
LOG_LEVEL=infoorwarn - Enable
RATE_LIMIT_ENABLED=true - Configure
CORS_ALLOWED_ORIGINS(restrict from*) - Enable
SERVER_ENABLE_COMPRESSION=true - Set appropriate
CACHE_TTLbased on data freshness needs - Use external configuration management (e.g., environment variables from secrets)
- Monitor logs and metrics
- Set up proper logging aggregation
Ensure the exchange name is valid and supported by CCXT:
curl http://localhost:8080/api/v1/exchange/availableIncrease RATE_LIMIT_RPS or implement client-side rate limiting.
Verify CACHE_ENABLED=true and check TTL with CACHE_TTL setting.
Adjust SERVER_READ_TIMEOUT and SERVER_WRITE_TIMEOUT based on network latency.
Key external dependencies:
- Gin - Web framework
- CCXT - Cryptocurrency exchange integration
- Zap - Structured logging
- Viper - Configuration management
- UUID - Unique request IDs
- Go Playground Validator - Input validation
Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Commit changes (
git commit -am 'Add my feature') - Push to branch (
git push origin feature/my-feature) - Create a Pull Request
This project is licensed under the MIT License. See LICENSE file for details.
For issues, questions, or contributions, please open an issue on the GitHub repository.
Built with ❤️ using Go and Gin Framework