The ultimate REST API for theme park data, ride information, and real-time queue times! π
Built with NestJS and TypeScript - a high-performance, modern API providing comprehensive access to detailed information about theme parks worldwide, including their attractions and current wait times.
This API integrates with queue-times.com to provide reliable and up-to-date information about theme park wait times and attraction data from around the world.
Experience the API live at https://api.park.fan - test it with real theme park data and interactive documentation! π―
# Clone the repository
git clone <repository-url>
cd api.park.fan
# Install dependencies (pnpm is super fast!)
pnpm install
# Configure environment
cp .env.example .env
# Edit .env with your database credentials
The API creates the database automatically! Just ensure PostgreSQL is running:
# The application automatically:
# 1. π Connects to PostgreSQL
# 2. ποΈ Creates database if it doesn't exist
# 3. π Executes migrations automatically
# 4. π‘ Starts data synchronization
# Development Mode with Hot Reload (for development)
pnpm run start:dev
# Production Build and Start
pnpm run build
pnpm run start:prod
# Debug Mode (for troubleshooting)
pnpm run start:debug
π― API Ready! Go to http://localhost:3000
for interactive documentation
Configure the API using environment variables in your .env
file:
Variable | Description | Default | Required |
---|---|---|---|
DB_HOST |
PostgreSQL Database Host | localhost |
β |
DB_PORT |
PostgreSQL Database Port | 5432 |
β |
DB_USER |
PostgreSQL Username | postgres |
β |
DB_PASS |
PostgreSQL Password | postgres |
β |
DB_NAME |
PostgreSQL Database Name | parkfan |
β |
PARK_OPEN_THRESHOLD_PERCENT |
Park "open" threshold (0-100%) | 50 |
β |
The Park Operating Status feature intelligently determines whether a park is "open" or "closed":
?openThreshold=X
parameterExamples:
# Use standard 50% threshold
GET /statistics
# Custom 75% threshold for stricter "open" definition
GET /statistics?openThreshold=75
# Relaxed 25% threshold
GET /parks?openThreshold=25
The Crowd Level feature provides intelligent real-time park congestion analysis:
Crowd Level Scale:
Configuration:
# Include crowd level (default)
GET /parks?includeCrowdLevel=true
# Skip crowd level for faster response
GET /parks?includeCrowdLevel=false
Provides current weather conditions and 7-day forecasts for each park location:
Configuration:
# Include weather data (default)
GET /parks?includeWeather=true
# Skip weather data for faster response
GET /parks?includeWeather=false
Data Source: Powered by Open-Meteo API with global coverage and WMO standard weather codes.
Method | Endpoint | Description |
---|---|---|
GET |
/ |
π Interactive API documentation (HTML) - Beautifully formatted! |
GET |
/readme |
π Raw documentation (Markdown) |
GET |
/openapi.yaml |
π OpenAPI 3.0.3 specification (YAML) |
π‘ Pro Tip: Import the OpenAPI specification into tools like Postman, Insomnia, or Swagger Editor for interactive API testing!
Method | Endpoint | Description |
---|---|---|
GET |
/parks |
π All parks with advanced filters & pagination |
GET |
/parks/:id |
π― Specific park with all ride details |
GET |
/parks/:id/rides |
π All rides for a specific park |
Method | Endpoint | Description |
---|---|---|
GET |
/parks/:continent |
π All parks in a continent |
GET |
/parks/:continent/:country |
π©πͺ All parks in a country |
GET |
/parks/:continent/:country/:park |
π° Access park via hierarchical path |
GET |
/parks/:continent/:country/:park/:ride |
π’ Access ride via hierarchical path |
Smart Routing:
/parks/30
β Park by ID)URL Transformation Rules:
-
).
) removed entirelyExamples:
/parks/europe
/parks/europe/germany
Phantasialand
β /parks/europe/germany/phantasialand
Europa Park
β /parks/europe/germany/europa-park
Islands Of Adventure At Universal Orlando
β /parks/north-america/united-states/islands-of-adventure-at-universal-orlando
Taron
ride β /parks/europe/germany/phantasialand/taron
Method | Endpoint | Description |
---|---|---|
GET |
/rides |
π All rides with filtering & search |
GET |
/rides/:id |
π― Specific ride with current queue status |
Method | Endpoint | Description |
---|---|---|
GET |
/statistics |
π Comprehensive statistics with geographic breakdowns |
Method | Endpoint | Description |
---|---|---|
GET |
/countries |
π©πͺ All countries with park counts |
GET |
/continents |
π All continents with park counts |
Method | Endpoint | Description |
---|---|---|
GET |
/status |
π API health check and system information |
/parks
)Parameter | Type | Description | Example |
---|---|---|---|
search |
string |
Search by park name or country | ?search=Disney |
country |
string |
Filter by specific country | ?country=Germany |
continent |
string |
Filter by continent | ?continent=Europe |
parkGroupId |
number |
Filter by park group | ?parkGroupId=1 |
openThreshold |
number |
Operational status threshold (0-100) | ?openThreshold=75 |
includeCrowdLevel |
boolean |
Include crowd level calculation | ?includeCrowdLevel=false |
includeWeather |
boolean |
Include live weather data | ?includeWeather=false |
page |
number |
Page number (β₯1) | ?page=2 |
limit |
number |
Results per page (max 100) | ?limit=20 |
/rides
)Parameter | Type | Description | Example |
---|---|---|---|
search |
string |
Search by ride name | ?search=coaster |
parkId |
number |
Filter by specific park | ?parkId=25 |
isActive |
boolean |
Filter by operational status | ?isActive=true |
page |
number |
Page number (β₯1) | ?page=3 |
limit |
number |
Results per page (max 100) | ?limit=50 |
/statistics
)Parameter | Type | Description | Example |
---|---|---|---|
openThreshold |
number |
Park operational threshold (0-100) | ?openThreshold=60 |
# Find Disney parks worldwide
GET https://api.park.fan/parks?search=Disney&limit=10
# All German parks
GET https://api.park.fan/parks?country=Germany
# European parks with relaxed "open" criteria
GET https://api.park.fan/parks?continent=Europe&openThreshold=25
# Parks with crowd level analysis (default)
GET https://api.park.fan/parks?search=Disney&includeCrowdLevel=true
# Fast response without crowd level calculation
GET https://api.park.fan/parks?country=Germany&includeCrowdLevel=false
# Fast response without weather data
GET https://api.park.fan/parks?country=Germany&includeWeather=false
# Complete data with both crowd level and weather (default)
GET https://api.park.fan/parks?search=Disney
# Minimal response - no crowd level or weather for maximum speed
GET https://api.park.fan/parks?includeCrowdLevel=false&includeWeather=false
# Parks in a specific group with pagination
GET https://api.park.fan/parks?parkGroupId=1&page=2&limit=5
# Get all parks in Europe
GET https://api.park.fan/parks/europe
# Get all parks in Germany
GET https://api.park.fan/parks/europe/germany
# Access Phantasialand via hierarchical path
GET https://api.park.fan/parks/europe/germany/phantasialand
# Access specific ride via hierarchical path
GET https://api.park.fan/parks/europe/germany/phantasialand/taron
# Access parks with complex names
GET https://api.park.fan/parks/north-america/united-states/islands-of-adventure-at-universal-orlando
# Access European park
GET https://api.park.fan/parks/europe/england/alton-towers/the-smiler
# Backward compatibility - access by ID
GET https://api.park.fan/parks/61
# Search for roller coasters
GET https://api.park.fan/rides?search=coaster&limit=20
# All rides at Disneyland Paris
GET https://api.park.fan/parks/26/rides
# Active rides only with pagination
GET https://api.park.fan/rides?isActive=true&page=1&limit=25
# Global theme park statistics
GET https://api.park.fan/statistics
# Statistics with strict "open" criteria (75%)
GET https://api.park.fan/statistics?openThreshold=75
# All countries with parks
GET https://api.park.fan/countries
# Continental breakdown
GET https://api.park.fan/continents
GET https://api.park.fan/parks/25
{
"id": 25,
"name": "Disneyland Park",
"country": "United States",
"continent": "North America",
"timezone": "America/Los_Angeles",
"latitude": 33.8121,
"longitude": -117.919,
"isActive": true,
"operatingStatus": {
"isOpen": true,
"openRideCount": 42,
"totalRideCount": 58,
"operatingPercentage": 72.4,
"openThreshold": 50
},
"weather": {
"current": {
"temperature": {
"min": 22,
"max": 28
},
"precipitationProbability": 10,
"weatherCode": 1,
"status": "partly_cloudy",
"weatherScore": 92
},
"forecast": [
{
"date": "2025-06-22",
"temperature": {
"min": 20,
"max": 30
},
"precipitationProbability": 5,
"weatherCode": 0,
"status": "sunny",
"weatherScore": 95
}
]
},
"crowdLevel": {
"level": 85,
"label": "Moderate",
"ridesUsed": 15,
"totalRides": 42,
"historicalBaseline": 35,
"currentAverage": 42,
"confidence": 82,
"calculatedAt": "2025-06-20T15:30:00Z"
},
"themeAreas": [
{
"id": 123,
"name": "Fantasyland",
"rides": [...]
}
]
}
GET https://api.park.fan/parks/europe/germany/phantasialand
{
"id": 61,
"name": "Phantasialand",
"country": "Germany",
"continent": "Europe",
"timezone": "Europe/Berlin",
"latitude": 50.7998,
"longitude": 6.8783,
"isActive": true,
"hierarchicalUrl": "/parks/europe/germany/phantasialand",
"operatingStatus": {
"isOpen": true,
"openRideCount": 8,
"totalRideCount": 12,
"operatingPercentage": 66.7,
"openThreshold": 50
},
"themeAreas": [
{
"id": 234,
"name": "Klugheim",
"rides": [
{
"id": 456,
"name": "Taron",
"hierarchicalUrl": "/parks/europe/germany/phantasialand/taron",
"isActive": true,
"waitTime": 45,
"lastUpdate": "2025-06-20T15:28:00Z"
}
]
}
]
}
GET https://api.park.fan/rides/456
{
"id": 456,
"name": "Taron",
"hierarchicalUrl": "/parks/europe/germany/phantasialand/taron",
"park": {
"id": 61,
"name": "Phantasialand",
"hierarchicalUrl": "/parks/europe/germany/phantasialand"
},
"themeArea": {
"id": 234,
"name": "Klugheim"
},
"isActive": true,
"waitTime": 45,
"lastUpdate": "2025-06-20T15:28:00Z"
}
GET https://api.park.fan/statistics
{
"global": {
"totalParks": 142,
"totalRides": 3247,
"openParks": 89,
"closedParks": 53,
"openPercentage": 62.7,
"openThreshold": 50
},
"longestWaitTimes": [
{
"park": {
"id": 25,
"name": "Disneyland Park",
"hierarchicalUrl": "/parks/north-america/united-states/disneyland-park"
},
"ride": {
"id": 789,
"name": "Space Mountain",
"hierarchicalUrl": "/parks/north-america/united-states/disneyland-park/space-mountain"
},
"waitTime": 120,
"lastUpdate": "2025-06-20T15:30:00Z"
}
],
"shortestWaitTimes": [
{
"park": {
"id": 61,
"name": "Phantasialand",
"hierarchicalUrl": "/parks/europe/germany/phantasialand"
},
"ride": {
"id": 456,
"name": "Taron",
"hierarchicalUrl": "/parks/europe/germany/phantasialand/taron"
},
"waitTime": 5,
"lastUpdate": "2025-06-20T15:25:00Z"
}
],
"busiestParks": [
{
"id": 25,
"name": "Disneyland Park",
"hierarchicalUrl": "/parks/north-america/united-states/disneyland-park",
"averageWaitTime": 75,
"operatingPercentage": 72.4
}
],
"quietestParks": [
{
"id": 61,
"name": "Phantasialand",
"hierarchicalUrl": "/parks/europe/germany/phantasialand",
"averageWaitTime": 18,
"operatingPercentage": 66.7
}
],
"byContinent": {
"North America": {
"totalParks": 45,
"openParks": 28,
"openPercentage": 62.2
},
"Europe": {
"totalParks": 67,
"openParks": 42,
"openPercentage": 62.7
},
"Asia": {
"totalParks": 30,
"openParks": 19,
"openPercentage": 63.3
}
}
}
src/
βββ main.ts # Application entry point
βββ app.module.ts # Root application module
βββ modules/
βββ parks/ # Parks module with weather integration
βββ rides/ # Rides management
βββ statistics/ # Analytics and statistics
βββ countries/ # Country data
βββ continents/ # Continental data
βββ database/ # Database configuration
βββ queue-times-parser/ # External data synchronization
βββ utils/ # Shared utilities and services
The API is production-ready and designed for horizontal scaling:
This project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.
For questions or support, please contact:
Built with β€οΈ for theme park enthusiasts worldwide π’