How I built a comprehensive RESTful API for Turkish Electronic Fund Trading Platform (TEFAS) using Go, Redis, and modern web scraping techniques and how to use it.
๐ Introduction
The Turkish Electronic Fund Trading Platform (TEFAS) is the central hub for mutual fund transactions in Turkey, processing billions of dollars in trades annually. While TEFAS provides a web interface for accessing fund data, there was no official API for developers and financial applications to programmatically access this wealth of information.
That's where tefas-api comes in - a high-performance, production-ready API that I built to bridge this gap. This article walks through the journey of creating a comprehensive financial data API that serves real-time fund information, historical data, and trading reports.
๐ฏ Project Overview
tefas-api is a RESTful API service that provides programmatic access to Turkish mutual fund data. Built with Go and following enterprise-grade architectural patterns, it offers:
- 50+ Portfolio Management Companies Data
- Real-time Fund Information
- Historical Performance Data
- Trading Volume Reports
- Institutional Analytics
- Interactive API Documentation
๐ Project Links
- Live Demo: API Documentation
- Developer: Serif Colakel
๐๏ธ Architecture & Technology Stack
Core Technologies
Backend Framework: Go with Gin HTTP framework
Caching: Redis for high-performance data caching
Web Scraping: ChromeDP for reliable data extraction
Documentation: OpenAPI 3.1 with Swagger UI
Containerization: Docker with multi-stage builds
Job Scheduling: Custom cron job management system
Architecture Pattern
The project follows the Standard Go Project Layout with clean architecture principles:
/cmd/api/ - Application entry point
/internal/ - Private application code
/config/ - Configuration management
/handler/ - HTTP request handlers (Gin)
/middleware/ - HTTP middleware (CORS, auth, rate limiting)
/model/ - Data models and DTOs
/repository/ - Data access layer (Redis caching)
/service/ - Business logic layer
/validator/ - Input validation
/pkg/ - Shareable library code
/logger/ - Structured logging
/scraper/ - Web scraping with ChromeDP
/cron/ - Job scheduling system
/api/ - OpenAPI/Swagger specifications
/web/ - Static assets and documentation
This structure ensures:
- Separation of Concerns: Each layer has a single responsibility
- Testability: Dependencies are injected and easily mockable
- Maintainability: Code is organized logically and follows Go conventions
- Scalability: New features can be added without affecting existing code
๐ Key Features
1. ๐ฐ Comprehensive Fund Data API
Fund Discovery
GET /api/v1/funds
Returns all available funds with their basic information.
Individual Fund Details
GET /api/v1/funds/{code}
Detailed fund information including performance metrics, portfolio composition, and historical data visualizations.
Portfolio Management Companies
GET /api/v1/funds/companies
Complete list of all 50+ portfolio management companies operating in Turkey:
{
"data": [
{
"code": "AKP",
"company": "AK PORTFรY YรNETฤฐMฤฐ A.ล."
},
{
"code": "ISP",
"company": "ฤฐล PORTFรY YรNETฤฐMฤฐ A.ล."
}
],
"meta": { "total": 50 },
"success": true
}
2. ๐ Performance & Analytics APIs
Fund Returns Analysis
GET /api/v1/funds/returns?fundType=1&startDate=01.01.2024&endDate=31.12.2024
Comprehensive return analysis with support for:
- Fund Type Filtering: Securities, Pension, ETF, Real Estate, Venture Capital
- Custom Date Ranges: Flexible date filtering
- Pagination Support: Handle large datasets efficiently
Fund Size Analytics
GET /api/v1/funds/sizes?fundType=1
Portfolio size analysis and growth metrics.
Historical Data
GET /api/v1/funds/historical?fundType=1&startDate=01.08.2025&endDate=16.08.2025
Time-series fund performance data with:
- 30-minute cache TTL for optimal performance
- Default 7-day range if dates not specified
- Pagination support for large datasets
Fund Comparison
GET /api/v1/funds/comparison?fundType=YAT&islemDurum=1
Management fee comparison across funds with detailed metrics.
3. ๐ Institutional Trading Reports
Trading Institutions Analytics
GET /api/v1/funds-reports/trading-institutions?year=2025&month=8
Statistics about institutions trading in TEFAS funds.
Transaction Volume Reports
- Total Transaction Volume: Market-wide transaction analysis
- Member-Based Volume: Institution-specific trading patterns
- Fund-Based Volume: Individual fund transaction statistics
- Member Stock Balances: Holdings analysis by fund type
4. โฐ Advanced Job Management
Built-in cron job system for automated data refresh:
POST /api/v1/cron/jobs
{
"name": "daily-fund-refresh",
"schedule": "0 2 * * *",
"function": "fund-data-refresh",
"enabled": true
}
Available Job Types:
-
fund-data-refresh
: Updates fund data cache -
fund-list-refresh
: Refreshes fund listings -
system-health-check
: Monitors system health
5. ๐ผ๏ธ Image Management System
Upload and manage company logos and assets:
POST /api/v1/images/
Content-Type: multipart/form-data
Features:
- Multiple Format Support: JPEG, PNG, GIF, WebP
- Secure UUID-based Naming: Prevents file conflicts
- Admin Authentication: Secure access control
- Automatic Serving: Direct URL access to images
๐ง Technical Implementation
Web Scraping with ChromeDP
One of the most challenging aspects was reliably extracting data from TEFAS web interface. I chose ChromeDP for its robustness:
func (s *Scraper) ScrapeFundData(ctx context.Context, fundCode string) (*model.Fund, error) {
ctx, cancel := chromedp.NewContext(ctx)
defer cancel()
var result string
err := chromedp.Run(ctx,
chromedp.Navigate(fmt.Sprintf("https://www.tefas.gov.tr/FonAnaliz.aspx?fon=%s", fundCode)),
chromedp.WaitVisible("#fund-data", chromedp.ByID),
chromedp.OuterHTML("#fund-data", &result),
)
if err != nil {
return nil, fmt.Errorf("scraping failed: %w", err)
}
return s.parseHTML(result)
}
Benefits of ChromeDP:
- JavaScript Execution: Handles dynamic content loading
- Reliable Waiting: Waits for elements to be visible
- Performance: Headless operation for speed
- Retry Logic: Built-in error handling and retries
Redis Caching Strategy
Intelligent caching system to minimize scraping load and improve response times:
func (r *Repository) GetFundWithCache(ctx context.Context, code string) (*model.Fund, error) {
cacheKey := fmt.Sprintf("fund:%s", code)
// Try cache first
cached, err := r.client.Get(ctx, cacheKey).Result()
if err == nil {
var fund model.Fund
if json.Unmarshal([]byte(cached), &fund) == nil {
return &fund, nil
}
}
// Cache miss - scrape fresh data
fund, err := r.scraper.ScrapeFundData(ctx, code)
if err != nil {
return nil, err
}
// Cache for 30 minutes
data, _ := json.Marshal(fund)
r.client.Set(ctx, cacheKey, data, 30*time.Minute)
return fund, nil
}
Cache Strategy:
- 30-minute TTL for fund data
- 6-hour TTL for static data
- 24-hour TTL for historical reports
- Memory caching for company data
Rate Limiting & Security
Comprehensive security measures to protect both the API and source systems:
func NewRateLimiter(requestsPerMinute, burstSize int) *RateLimiter {
return &RateLimiter{
clients: make(map[string]*clientLimiter),
rate: rate.Limit(requestsPerMinute),
burstSize: burstSize,
mutex: &sync.RWMutex{},
}
}
func (rl *RateLimiter) RateLimit() gin.HandlerFunc {
return func(c *gin.Context) {
clientIP := c.ClientIP()
if !rl.allowRequest(clientIP) {
c.JSON(http.StatusTooManyRequests, gin.H{
"error": "Rate limit exceeded",
"retry_after": 60,
})
c.Abort()
return
}
c.Next()
}
}
Security Features:
- IP-based Rate Limiting: 100 requests/minute with 20 burst capacity
- Admin Authentication: Multiple auth methods (Bearer, Header, Query)
- CORS Protection: Configurable cross-origin policies
- Request Logging: Comprehensive audit trails
- Input Validation: Strict parameter validation
Error Handling & Logging
Structured logging and comprehensive error handling:
type APIError struct {
Error string `json:"error"`
Details string `json:"details,omitempty"`
Status int `json:"status,omitempty"`
CorrelationID string `json:"correlationId,omitempty"`
Suggestion string `json:"suggestion,omitempty"`
Severity string `json:"severity,omitempty"`
OccurredAt time.Time `json:"occurredAt,omitempty"`
}
func (h *Handler) errorResponse(c *gin.Context, status int, message string, details string) {
correlationID := c.GetString("request_id")
h.logger.ErrorWithFields("API Error", map[string]interface{}{
"status": status,
"message": message,
"details": details,
"correlation_id": correlationID,
"client_ip": c.ClientIP(),
"user_agent": c.GetHeader("User-Agent"),
})
c.JSON(status, APIError{
Error: message,
Details: details,
Status: status,
CorrelationID: correlationID,
OccurredAt: time.Now(),
})
}
๐ Interactive Documentation
OpenAPI 3.1 Specification
The API includes comprehensive OpenAPI documentation with:
/api/v1/funds/companies:
get:
tags:
- Funds
summary: Get All Portfolio Management Companies
description: "Retrieves a list of all portfolio management companies with their codes and names"
responses:
"200":
description: "Successful response"
content:
application/json:
schema:
$ref: "#/components/schemas/CompanyResponse"
Swagger UI Integration
Interactive documentation available at multiple endpoints:
-
Main Documentation:
http://localhost:8080/docs
-
Swagger Redirect:
http://localhost:8080/swagger
-
API Root:
http://localhost:8080/
Features:
- Live Testing: Try endpoints directly from browser
- Request/Response Examples: See actual API calls
- Parameter Documentation: Detailed descriptions
- Authentication Testing: Built-in auth support
๐ณ Docker & Deployment
Multi-stage Docker Build
Optimized Docker configuration for production deployment:
# Build stage
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o api cmd/api/main.go
# Production stage
FROM alpine:latest
RUN apk --no-cache add ca-certificates chromium
WORKDIR /root/
COPY --from=builder /app/api .
COPY --from=builder /app/api ./api/
COPY --from=builder /app/web ./web/
EXPOSE 8080
CMD ["./api"]
Docker Compose Configuration
version: "3.8"
services:
api:
build: .
ports:
- "8080:8080"
environment:
- REDIS_ADDR=redis:6379
- ADMIN_SECRET_KEY=${ADMIN_SECRET_KEY}
depends_on:
- redis
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
volumes:
redis_data:
๐ Performance Metrics
Response Times
- Company Data: < 10ms (in-memory)
- Cached Fund Data: < 50ms
- Fresh Fund Data: < 2s (includes scraping)
- Report Generation: < 500ms
Throughput
- Rate Limit: 100 requests/minute per IP
- Burst Capacity: 20 requests
- Concurrent Users: 1000+ supported
- Cache Hit Ratio: 85%+ during normal operation
Resource Usage
- Memory: ~50MB base usage
- CPU: < 5% during normal load
- Redis: ~100MB for full cache
- Storage: < 1GB including logs
๐ฎ Future Enhancements
Planned Features
- Real-time WebSocket API: Live fund price updates
- GraphQL Interface: Flexible data querying
- Machine Learning: Price prediction models
- Mobile SDK: Native mobile app integration
- Data Export: CSV, Excel, PDF report generation
- Advanced Analytics: Technical indicators and charts
Scalability Improvements
- Microservice Architecture: Split into specialized services
- Database Integration: PostgreSQL for historical data
- Message Queues: Redis Streams/RabbitMQ for async processing
- Load Balancing: Multiple API instances
- CDN Integration: Global content distribution
๐ก Lessons Learned
Technical Insights
- Web Scraping Reliability: ChromeDP proved more reliable than HTTP-only scraping for dynamic content
- Caching Strategy: 30-minute TTL provided the optimal balance between freshness and performance
- Error Handling: Comprehensive error responses significantly improved API usability
- Documentation: Interactive Swagger UI dramatically increased adoption
Go-Specific Learnings
- Project Structure: Standard Go Project Layout greatly improved maintainability
- Interface Design: Small, focused interfaces made testing much easier
- Context Usage: Proper context handling was crucial for timeout management
- Error Wrapping: Go 1.13+ error wrapping improved debugging significantly
๐ Conclusion
Building the TEFAS API has been an incredible journey that combined my passion for financial technology with Go's powerful ecosystem. The project demonstrates how modern web scraping, intelligent caching, and clean architecture can create a production-ready API that serves critical financial data.
Key Achievements:
- โ 50+ Portfolio Management Companies accessible via API
- โ Comprehensive Fund Data with real-time updates
- โ Production-Ready Architecture with Docker deployment
- โ Interactive Documentation with Swagger UI
- โ Enterprise-Grade Security with rate limiting and authentication
- โ High Performance with Redis caching and optimizations
Impact & Usage
Impact & Usage
The API has already been used by:
- Financial Applications: Portfolio management tools
- Research Platforms: Academic financial research
- Trading Bots: Automated investment strategies
- Mobile Apps: Personal finance applications
Connect with Me and Use the API
- Quick Start: API Documentation
- GitHub: @serifcolakel
- LinkedIn: Serif Colakel
- Dev.to: @serifcolakel
- Email: serifcolakel0@gmail.com
Thank you for reading! If you found this article helpful, please give it a clap and consider following me for more content about Go, fintech, and software architecture.
Tags: #golang #fintech #api #webscraping #redis #docker #swagger #turkish #finance #tefas #software #development
Top comments (0)