A FastAPI-based analytics platform that aggregates and analyzes data from Polymarket to provide insights into market dynamics and top trader behaviors.
Sympoly API fetches data from Polymarket's public API to compute statistics about prediction markets, focusing on:
- Active market dynamics and sentiment analysis
- Top trader positions and behaviors
- Consensus metrics derived from trader positions
- Market liquidity and volume statistics
The API runs background workers that periodically fetch data from Polymarket and store it in a PostgreSQL database, making it available through REST endpoints for frontend consumption.
- Real-time Market Stats: Aggregated statistics on active prediction markets
- Smart Consensus Calculation: Analyzes top traders' positions to identify market sentiment
- Position Tracking: Monitors positions of top-performing traders across all active markets
- Market Search: Fast autocomplete search for active markets
- Async Data Pipeline: Background workers continuously sync data from Polymarket API
- Framework: FastAPI (Python 3.14)
- Database: PostgreSQL with SQLAlchemy (async)
- Task Queue: Celery with Redis broker
- Scheduler: Celery Beat for periodic tasks
- API Client: Custom Polymarket API wrapper
- Data Processing: Pandas for statistics computation
sympoly-api/
├── main.py # FastAPI application entry point
├── alembic/ # Database migrations
│ └── versions/ # Migration scripts
├── app/
│ ├── celery_app.py # Celery configuration and beat schedule
│ ├── config.py # Application settings
│ ├── database.py # Database connection and session management
│ ├── utils.py # Utility functions
│ ├── libs/
│ │ └── polymarket_api.py # Polymarket API client
│ ├── models/ # SQLAlchemy ORM models
│ │ ├── base.py # Base model class
│ │ ├── batch.py # Batch tracking models
│ │ └── polymarket.py # Polymarket data models
│ ├── routers/ # API route handlers
│ │ └── symbiosis.py # Main API endpoints
│ ├── schemas/ # Pydantic models for request/response
│ │ └── symbiosis.py # API schemas
│ ├── services/ # Business logic layer
│ │ ├── active_market_service.py
│ │ ├── batch_service.py
│ │ ├── leaderboard_service.py
│ │ ├── symbiosis_service.py
│ │ └── user_position_service.py
│ └── tasks/ # Celery background tasks
│ ├── active_markets_worker.py # Fetches active markets
│ ├── leaderboard_worker.py # Fetches top traders
│ └── user_positions_worker.py # Fetches trader positions
└── scripts/
└── notebooks/ # Jupyter notebooks for analysis
The application uses Celery Beat to schedule periodic data synchronization tasks:
- Task:
fetch_active_markets - Function: Fetches all active prediction markets from Polymarket
- Frequency: Configurable (currently disabled in production)
- Processing: Filters markets by active status and end date
- Task:
fetch_leaderboard - Function: Retrieves the monthly leaderboard of top traders
- Frequency: Configurable (currently disabled in production)
- Processing: Fetches top 5000 traders ranked by performance
- Task:
fetch_user_positions - Function: Collects all positions for top traders across active markets
- Frequency: Configurable via
user_positions_intervalsetting - Processing:
- Fetches positions for all top traders
- Filters positions by active markets only
- Deduplicates and stores in database
- Takes ~20 minutes for 5000 users (300K+ positions)
Each task includes:
- Automatic retry with exponential backoff
- Error handling and logging
- Batch tracking to prevent duplicate processing
- Database transaction management
- Development:
http://localhost:8000
Root endpoint with API information.
Response:
{
"message": "Welcome to Sympoly API",
"version": "0.1.0",
"docs": "/docs"
}Get symbiosis statistics for markets based on top trader positions.
Query Parameters:
top_n_traders(int, default=5000): Number of top traders to include (1-5000)sort_by(string, default="count_traders"): Column to sort by- Options:
count_traders,smart_consensus,median_trade_price,median_pnl_pct, etc.
- Options:
sort_asc(bool, default=false): Sort order (true=ascending, false=descending)page(int, default=1): Page number (1-indexed)page_size(int, default=50): Items per page (max 100)slug(string, optional): Filter by specific market slug
Response:
{
"total": 9372,
"page": 1,
"page_size": 50,
"total_pages": 188,
"items": [
{
"slug": "market-slug",
"question": "Market question?",
"count_traders": 149,
"label_outcome_1": "Yes",
"count_outcome_1": 89,
"pct_outcome_1": 59.73,
"label_outcome_2": "No",
"count_outcome_2": 60,
"pct_outcome_2": 40.27,
"smart_consensus": 59.73,
"liquidity": 123456.78,
"volume": 987654.32,
"endDate": "2025-12-31T23:59:59Z"
}
]
}Search for active markets by question text (optimized for autocomplete).
Query Parameters:
q(string, required): Search query (min 1 character, case-insensitive)limit(int, default=6): Maximum results (max 20)
Response:
{
"items": [
{
"slug": "market-slug",
"question": "Market question?",
"icon": "https://...",
"endDate": "2025-12-31T23:59:59Z"
}
]
}Get user positions for a specific market, balanced by outcome.
Path Parameters:
slug(string, required): Market slug
Query Parameters:
top_n_traders(int, default=5000): Number of top traders (1-5000)page(int, default=1): Page numberpage_size(int, default=50): Items per page (max 100)
Response:
{
"total": 149,
"page": 1,
"page_size": 50,
"total_pages": 3,
"items": [
{
"proxyWallet": "0x...",
"userName": "TraderName",
"rank": 42,
"outcome": "Yes",
"size": 1234.56,
"avgPrice": 0.65,
"cashPnl": 123.45,
"percentPnl": 12.34
}
]
}- Swagger UI:
/docs - ReDoc:
/redoc
- Python 3.14+
- Poetry
- Docker
- Clone the repository
git clone <repository-url>
cd sympoly-api- Install dependencies with Poetry
poetry install-
Configure environment variables Create a
.envfile -
Run database migrations
poetry run alembic upgrade head- Start the API server
poetry run uvicorn main:app --reload- Start Celery worker (in another terminal)
poetry run celery -A app.celery_app worker --loglevel=info -Q polymarket- Start Celery Beat scheduler (in another terminal)
poetry run celery -A app.celery_app beat --loglevel=infoAnalysis notebooks are available in scripts/notebooks/ for data exploration.