Real-time theme park intelligence powered by machine learning
Aggregating wait times, weather forecasts, park schedules, and ML-powered predictions for optimal theme park experiences worldwide.
- β¨ Features
- π Quick Start
- π API Documentation
- π οΈ Tech Stack
- π Project Structure
- π³ Docker Commands
- π§ͺ Testing
- π§ Environment Variables
- π Real-time Wait Times β Live queue data for attractions, shows, and restaurants
- π€ ML Predictions β Machine learning forecasts for wait times and crowd levels
- π€οΈ Weather Integration β Current conditions and 16-day forecasts for all parks
- π Park Schedules β Opening hours, special events, and operating calendars
- π΅οΈ Smart Gaps β Reconstruction of historical opening hours from ride activity when official data is missing
- π Multi-Source Data β Aggregated from multiple providers for maximum coverage
- π Analytics Ready β TimescaleDB-powered time-series data for insights
- β‘ High Performance β Redis caching and Bull queue processing
- π― RESTful API β Clean endpoints with full Swagger/OpenAPI documentation
| Category | Technology |
|---|---|
| Backend | NestJS 11 Β· TypeScript (strict mode) |
| Database | PostgreSQL 16 Β· TimescaleDB |
| Cache & Queue | Redis 7 Β· Bull Queue |
| ML Service | Python 3.11 Β· CatBoost Β· FastAPI |
| DevOps | Docker Compose Β· GitHub Actions |
| Testing | Jest Β· Supertest Β· Testcontainers |
The API implements advanced logic to handle parks without official API schedules (e.g., Efteling, Hellendoorn) and seasonal closures.
All park objects include a hasOperatingSchedule boolean:
true: The park provides official operating hours via an API.false: The park does not provide official hours. Opening times are either null (future) or reconstructed from activity (past).
For past days without official data, the system reconstructs opening hours from ride activity. See the Smart Gaps Documentation for technical details.
- Opening Time: First 15min window with >= 10% ride activity, rounded down.
- Closing Time: Last 15min window with activity, rounded up.
- Rounding: Both times are rounded to the nearest full hour for maximum plausibility.
The system automatically identifies "Seasonal Parks" (parks with winter gaps > 21 days).
- Seasonal Parks: Future dates outside the known operating range are marked as
CLOSED(suppressing crowd predictions). - Year-Round Parks: Future dates without schedules remain
UNKNOWN, allowing ML-powered crowd and wait-time predictions for trip planning.
- Node.js 20+
- Docker & Docker Compose
- Git
# Clone the repository
git clone https://github.com/PArns/v4.api.park.fan.git
cd v4.api.park.fan
# Install dependencies
npm install
# Copy environment configuration
cp .env.example .env
# Start infrastructure (PostgreSQL + Redis)
npm run docker:up
# Start development server
npm run devOnce running, you can access:
- API Root: http://localhost:3000/ (this README)
- API Base: http://localhost:3000/v1
- API Docs: http://localhost:3000/api (Swagger)
System health checks and monitoring endpoints.
GET /v1/health # System health status
GET /v1/health/db # Database connectivityResponse includes:
- System uptime
- Database connection status
- Last sync timestamps (wait times, park metadata)
- Active jobs and queue status
Core endpoints for park information, weather, schedules, and wait times. All routes use the full geographic path structure for consistency and SEO-friendly URLs.
Geographic Routes:
GET /v1/parks # List all parks (paginated)
GET /v1/parks/:continent # Parks by continent
GET /v1/parks/:continent/:country # Parks by country
GET /v1/parks/:continent/:country/:city # Parks by city
GET /v1/parks/:continent/:country/:city/:parkSlug # Get park by location
GET /v1/parks/:continent/:country/:city/:parkSlug/calendar # Integrated calendar
GET /v1/parks/:continent/:country/:city/:parkSlug/weather # Current weather & history
GET /v1/parks/:continent/:country/:city/:parkSlug/weather/forecast # 16-day forecast
GET /v1/parks/:continent/:country/:city/:parkSlug/schedule # Operating hours
GET /v1/parks/:continent/:country/:city/:parkSlug/wait-times # Live wait times
GET /v1/parks/:continent/:country/:city/:parkSlug/predictions/yearly # Yearly crowd predictions
GET /v1/parks/:continent/:country/:city/:parkSlug/attractions # List park attractions
GET /v1/parks/:continent/:country/:city/:parkSlug/attractions/:attractionSlug # Get attractionQuery Parameters:
continent,country,cityβ Filter by location (in list endpoint)sortβ Sort order (name, popularity, etc.)page,limitβ Pagination (default: page=1, limit=10)from,toβ Date range for weather/schedule (YYYY-MM-DD format)
Example Geographic Route:
GET /v1/parks/north-america/united-states/orlando/magic-kingdomExample Response:
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "Magic Kingdom",
"slug": "magic-kingdom",
"url": "/v1/parks/north-america/united-states/orlando/magic-kingdom",
"continent": "North America",
"country": "United States",
"city": "Orlando",
"timezone": "America/New_York",
"currentStatus": "OPERATING",
"currentLoad": {
"crowdLevel": "moderate",
"occupancy": 0.65
},
"coordinates": { "lat": 28.3772, "lng": -81.5707 },
"attractions": [
{
"id": "...",
"name": "Space Mountain",
"slug": "space-mountain",
"url": "/v1/parks/north-america/united-states/orlando/magic-kingdom/attractions/space-mountain"
}
]
}Detailed attraction data with ML predictions and historical analytics. Access attractions through their park's geographic route.
Routes:
GET /v1/parks/:continent/:country/:city/:parkSlug/attractions # List park attractions
GET /v1/parks/:continent/:country/:city/:parkSlug/attractions/:attractionSlug # Get attraction detailsQuery Parameters:
page,limitβ Pagination for attraction list (default: page=1, limit=10)
Response includes:
- Live wait times and status (OPERATING, CLOSED, DOWN, REFURBISHMENT)
- 24-hour ML-powered wait time forecasts
- Daily predictions with confidence scores
- Historical statistics (average, percentiles)
- Downtime tracking and reliability metrics
- Full geographic URL for easy navigation
Example Response:
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "Space Mountain",
"slug": "space-mountain",
"url": "/v1/parks/north-america/united-states/orlando/magic-kingdom/attractions/space-mountain",
"park": {
"id": "...",
"name": "Magic Kingdom",
"slug": "magic-kingdom"
},
"category": "RIDE",
"currentWaitTime": 45,
"status": "OPERATING",
"lastUpdate": "2025-12-23T19:30:00Z",
"forecast": [
{ "hour": "20:00", "predictedWaitTime": 50, "confidence": 0.87 },
{ "hour": "21:00", "predictedWaitTime": 35, "confidence": 0.82 }
],
"stats": {
"averageWaitTime": 42,
"p50": 40,
"p75": 55,
"p90": 70
}
}Navigate parks by geographic hierarchy for route generation and exploration.
GET /v1/discovery/geo # Full geo hierarchy
GET /v1/discovery/continents # List continents
GET /v1/discovery/continents/:continent # Countries in continentResponse Structure:
{
"continents": [
{
"continent": "North America",
"countries": [
{
"country": "United States",
"cities": [
{
"city": "Orlando",
"parks": [
{
"name": "Magic Kingdom",
"url": "/north-america/united-states/orlando/magic-kingdom",
"status": "OPERATING",
"analytics": {
"statistics": {
"avgWaitTime": 35,
"operatingAttractions": 42,
"closedAttractions": 3,
"totalAttractions": 45,
"crowdLevel": "moderate"
}
},
"attractions": [...]
}
]
}
]
}
]
}
]
}Note:
analytics.statistics.crowdLevelis co-present withavgWaitTimewhenever the park has a valid P50 baseline. See P50 Crowd Levels for the rating system.
Resort-level aggregation grouping multiple parks. Destinations are used internally for data organization but are not exposed as top-level API endpoints. Parks are accessed directly via their geographic routes.
Examples:
- Walt Disney World (Magic Kingdom, EPCOT, Hollywood Studios, Animal Kingdom)
- Disneyland Paris (Disneyland Park, Walt Disney Studios Park)
Entertainment and dining options are available through the park endpoints. Shows and restaurants are not exposed as top-level API endpoints but are included in park responses and can be accessed via the search endpoint.
Global search with enriched results across parks and attractions.
GET /v1/search?q=disney # Search all types
GET /v1/search?q=thunder&type=attraction # Filter by type
GET /v1/search?q=paris # Search by citySearch Features:
- Multi-entity search: Parks and attractions
- Geographic search: By city, country, or continent
- Per-type counts: Shows returned vs total results
- Enriched results: Coordinates, wait times, park hours, full geographic URLs
- Smart filtering: Type-based filtering with max 5 results per type
- Fast response: Redis-cached for 5min, <3ms cached response
Response Structure:
{
"query": "disney",
"counts": {
"park": {"returned": 5, "total": 13},
"attraction": {"returned": 5, "total": 156}
},
"results": [
{
"type": "park",
"name": "Disneyland Park",
"status": "OPERATING",
"load": "normal",
"parkHours": {...},
"coordinates": {...}
},
{
"type": "attraction",
"name": "Space Mountain",
"waitTime": 45,
"load": "higher",
"parentPark": {...}
}
]
}Public holiday data affecting park crowds and operating hours. Holidays are used internally for ML predictions and analytics but are not exposed as top-level API endpoints.
v4.api.park.fan/
βββ src/
β βββ config/ # App configuration & environment
β βββ common/ # Shared utilities, filters, interceptors
β βββ database/ # Database utilities & migrations
β βββ queues/ # Bull queue setup & processors
β βββ health/ # Health check endpoints
β βββ destinations/ # Resort/destination grouping
β βββ parks/ # Parks, weather, schedules
β βββ attractions/ # Attractions & data sources
β βββ shows/ # Entertainment shows
β βββ restaurants/ # Dining locations
β βββ queue-data/ # Wait time data & history
β βββ ml/ # ML prediction integration
β βββ analytics/ # Statistics & analytics
β βββ holidays/ # Public holiday data
β βββ date-features/ # Date-based features for ML
β βββ discovery/ # Geographic discovery endpoints
β βββ search/ # Global search functionality
βββ ml-service/ # Python ML service (CatBoost)
β βββ train.py # Model training script
β βββ inference.py # FastAPI prediction service
β βββ features.py # Feature engineering
β βββ db.py # Database connection
βββ docker/ # Docker configurations
βββ scripts/ # Utility & debug scripts
βββ migrations/ # Database migrations
βββ test/ # E2E tests
# Start all services (PostgreSQL + Redis)
npm run docker:up
# Stop all services
npm run docker:down
# View logs
npm run docker:logs
# Restart services
npm run docker:restart
# Reset database (WARNING: deletes all data)
npm run db:resetProduction Deployment:
docker-compose -f docker-compose.production.yml up -d# Run unit tests
npm run test
# Run e2e tests
npm run test:e2e
# Run all tests with coverage
npm run test:all:cov
# Watch mode for development
npm run test:watch
# Specific test file
npm run test -- wait-times.processor.spec.tsCode Quality:
# Lint code
npm run lint
# Format code
npm run format
# Type check
npm run buildNODE_ENV=development # development | production | test
PORT=3000 # API server port
API_PREFIX=v1 # API version prefix# PostgreSQL with TimescaleDB
DB_HOST=localhost
DB_PORT=5432
DB_USERNAME=parkfan
DB_PASSWORD=your_secure_password
DB_DATABASE=parkfan
DB_SYNCHRONIZE=true # β οΈ Set to false in production!
DB_LOGGING=false # Enable for debugging
DB_SSL_ENABLED=false # Enable for production# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD= # Optional, recommended for production
# Bull Queue
BULL_PREFIX=parkfan# Google APIs (Geocoding, Places)
GOOGLE_API_KEY=your_google_api_key
# Weather Data
OPEN_WEATHER_API_KEY=your_openweather_key
# Data Sources (optional, for enhanced coverage)
QUEUE_TIMES_API_KEY= # Queue-Times.com
THEMEPARKS_API_KEY= # ThemeParks.wiki# ML Service Configuration
ML_SERVICE_URL=http://localhost:8000 # Development
# ML_SERVICE_URL=http://ml-service:8000 # Production (Docker)
MODEL_DIR=/app/models # Model storage directory
MODEL_VERSION=v1.1.0 # Current model version# Data Sync Intervals (cron expressions)
SYNC_WAIT_TIMES_CRON=*/5 * * * * # Every 5 minutes
SYNC_PARK_METADATA_CRON=0 6 * * * # Daily at 6 AM
SYNC_WEATHER_CRON=0 * * * * # HourlyThis is a private project. For questions or collaboration inquiries, please contact the maintainer.
UNLICENSED β Private project by Patrick Arns
This project aggregates data from multiple sources:
- Queue-Times.com β Real-time wait time data
- ThemeParks.wiki β Comprehensive park information and live data
- Wartezeiten.app β Wait times, crowd levels, and opening hours enrichment
Special thanks to these services for making real-time theme park data accessible.
Made with β€οΈ for theme park enthusiasts worldwide