Windnavigator API Documentation
Overview
This documentation covers the unified Python implementation for accessing UL Renewables API endpoints, specifically the COMPASS stats and Global Reanalysis services.
API Configuration
API_KEY = 'your-api-key-here'
API_URL = 'https://ul-renewables.com/api'
Authentication
All requests require a valid API key passed in the request payload under the key parameter.
COMPASS Stats API
Description
The COMPASS stats endpoint provides comprehensive wind resource statistics for a specific location and height.
Request Method
POST request to https://ul-renewables.com/api
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| key | string | Yes | Your API authentication key |
| product | string | Yes | Must be "stats" for statistical data |
| latitude | float | Yes | Latitude coordinate (-90 to 90) |
| longitude | float | Yes | Longitude coordinate (-180 to 180) |
| height | integer | Yes | Hub height in meters |
Example Request
compass_payload = {
"key": "your-api-key",
"product": "stats",
"latitude": 36.12361,
"longitude": -95.37542,
"height": 250,
}
Response Format
The COMPASS stats API returns a JSON object with comprehensive wind resource data:
{
"elevation": 189.9,
"roughness": 0.045,
"ghi": 1660.699951171875,
"hubheight": 250,
"windSpeed": 9.06,
"windShear": 0.254,
"weibullA": 10.22,
"weibullK": 2.035,
"temperature": 13.3,
"airDensity": 1.161,
"meanPowerDensity": 809.9,
"rose": [0.075, 0.083, 0.051, 0.038, 0.025, 0.029, 0.052, 0.158, 0.251, 0.083, 0.038, 0.018, 0.012, 0.016, 0.023, 0.049],
"monthly": [10.35, 10.07, 9.91, 10.6, 9.38, 7.23, 6.71, 7.67, 7.06, 9.32, 10.47, 10.01],
"latitude": 36.12361,
"longitude": -95.37542,
"data_version": "2025"
}
Response Fields Description
| Field | Unit | Description |
|---|---|---|
| elevation | meters | Ground elevation above sea level |
| roughness | - | Surface roughness coefficient |
| ghi | kWh/m²/year | Global Horizontal Irradiance (annual) |
| hubheight | meters | Requested hub height |
| windSpeed | m/s | Mean annual wind speed |
| windShear | - | Wind shear exponent |
| weibullA | m/s | Weibull scale parameter |
| weibullK | - | Weibull shape parameter |
| Field | Unit | Description |
|---|---|---|
| temperature | °C | Mean annual temperature |
| airDensity | kg/m³ | Mean air density |
| meanPowerDensity | W/m² | Mean power density |
| rose | - | Wind rose data (16 directional bins) |
| monthly | m/s | Monthly average wind speeds (Jan-Dec) |
| latitude | degrees | Requested latitude |
| longitude | degrees | Requested longitude |
| data_version | - | Version of the underlying dataset |
Global Reanalysis API
Description
The Global Reanalysis endpoint provides historical time series meteorological data from various reanalysis datasets.
Request Method
POST request to https://ul-renewables.com/api
Available Datasets
ERA5-100
ERA5 reanalysis at 100m height with high temporal resolution
ERA5-10
ERA5 reanalysis at 10m height with high temporal resolution
MERRA2-50
MERRA-2 reanalysis at 50m height with high spatial resolution
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| key | string | Yes | Your API authentication key |
| product | string | Yes | Must be "reanalysis" for time series data |
| latitude | float | Yes | Latitude coordinate (-90 to 90) |
| longitude | float | Yes | Longitude coordinate (-180 to 180) |
| dataset | string | Yes | One of: era5-100, era5-10, merra2-50 |
Example Request
reanalysis_payload = {
'key': 'your-api-key',
'latitude': 39.38832449859204,
'longitude': -97.63412475585938,
'dataset': 'era5-100',
'product': 'reanalysis',
}
Response Format
The Global Reanalysis API returns a CSV time series file containing historical meteorological data with comprehensive metadata headers.
Example Response Structure:
ERA5 data downloaded from Windnavigator on 2025-07-07 11:09:19 EDT
URL: https://windnavigator.ul-renewables.com
Data set: ERA5 (1979-01-01T00:00:00Z - 2025-06-15T23:00:00Z)
Source: European Centre for Medium-Range Weather Forecasts
Provider: UL Services Group LLC
Latitude: 39.388324
Longitude: -97.634125
Height: 100m
Each time stamp indicates the beginning of a time step.
UL Services Group LLC provides this data as-is from source and does not guarantee its accuracy.
Date/time [UTC],Speed_100m [m/s],Direction_100m [degrees],Temperature_2m [degrees C],Pressure_0m [kPa]
1979-01-01T00:00:00,8.08712,350.31674,-13.28293,97.38020
1979-01-01T01:00:00,8.34000,355.27872,-13.28602,97.43256
1979-01-01T02:00:00,8.59824,1.43736,-13.37751,97.46263
...
Response Components
Header Information
- • Download timestamp and source URL
- • Dataset name and temporal coverage
- • Data source and provider information
- • Exact coordinates and measurement height
- • Data quality disclaimer
Data Columns
| Column | Unit | Description |
|---|---|---|
| Date/time [UTC] | ISO 8601 | Timestamp in UTC |
| Speed_100m [m/s] | m/s | Wind speed at height |
| Direction_100m [degrees] | degrees | Wind direction |
| Temperature_2m [degrees C] | °C | Air temperature |
| Pressure_0m [kPa] | kPa | Surface pressure |
Data Processing Example
import pandas as pd
from io import StringIO
# After getting the response from Global Reanalysis API
if response.status_code == 200:
# Skip header lines and read CSV data
csv_data = response.text
# Find where the actual CSV data starts (after the header comments)
lines = csv_data.split('\n')
csv_start = 0
for i, line in enumerate(lines):
if line.startswith('Date/time'):
csv_start = i
break
# Read the CSV data into a pandas DataFrame
csv_content = '\n'.join(lines[csv_start:])
df = pd.read_csv(StringIO(csv_content))
# Convert datetime column
df['Date/time [UTC]'] = pd.to_datetime(df['Date/time [UTC]'])
# Now you can analyze the time series data
print(f"Data range: {df['Date/time [UTC]'].min()} to {df['Date/time [UTC]'].max()}")
print(f"Mean wind speed: {df['Speed_100m [m/s]'].mean():.2f} m/s")
print(f"Total records: {len(df)}")
Temporal Coverage
ERA5 Datasets
1979-01-01 to near-present (updated regularly)
MERRA-2 Datasets
1980-01-01 to near-present (updated regularly)
Resolution: Hourly data for all datasets
Time zone: All timestamps are in UTC
WRF Time Series API
Description
3km resolution on-demand time series for improved long-term adjustment of measurements. Supports multiple points in a single request.
Request Method
POST request to https://ul-renewables.com/api/wrfts/
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| key | string | Yes | API write access key |
| string | Yes | User email address | |
| point_list | array | Yes | Array of coordinate pairs in "latitude,longitude" format |
| height | array | Yes | Array of height levels (10-300m) as strings |
| start_date | string | Yes | Start date in YYYY-MM format |
| end_date | string | Yes | End date in YYYY-MM format (must be after start_date) |
| temp_resolution | string | Yes | Temporal resolution: '10', '15', or '60' minutes |
| name | string | No | Custom location name (defaults to address) |
Parameter Details
Point List Format
Each point should be formatted as "latitude,longitude":
["45.55667788,21.33445566", "45.55667788,23.33445566"]
Height Levels
Heights as string array:
["100", "150", "200"]
Example Request
wrfts_payload = {
"key": "your-api-key",
"email": "user@example.com",
"point_list": [
"45.55667788,21.33445566",
"45.55667788,23.33445566",
"43.55667788,21.33445566",
"43.55667788,23.33445566"
],
"height": ["100", "150", "200"],
"start_date": "2023-01",
"end_date": "2023-12",
"temp_resolution": "60",
"name": "Multi-Point Wind Farm Site"
}
Response Format
{
"status": "ok",
"job_id": 12345
}
Wind Resource Grid 200m API
Description
Generate wind resource grid data at 200m resolution within a 50km radius buffer around the specified point.
Request Method
POST request to https://ul-renewables.com/api/wrg200
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| key | string | Yes | API write access key |
| name | string | No | Project name |
| string | Yes | User email address | |
| latitude | float | Yes | Center latitude coordinate |
| longitude | float | Yes | Center longitude coordinate |
| height | array | Yes | Array of height levels (10-300m) as strings |
Example Request
wrg200_payload = {
"key": "your-api-key",
"name": "Wind Resource Study",
"email": "user@example.com",
"latitude": 36.12361,
"longitude": -95.37542,
"height": ["100", "150", "200"]
}
Response Format
{
"status": "ok",
"job_id": 12346
}
Wind Resource Grid 50m/Sitewind® API
Description
Generate high-resolution wind resource grid data at 50m resolution for a custom defined rectangular area with advanced configuration options.
Request Method
POST request to https://ul-renewables.com/api/wrg50
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| key | string | Yes | API write access key |
| name | string | No | Project name |
| string | Yes | User email address | |
| longitude1 | float | Yes | First corner longitude coordinate |
| latitude1 | float | Yes | First corner latitude coordinate |
| longitude2 | float | Yes | Second corner longitude coordinate |
| latitude2 | float | Yes | Second corner latitude coordinate |
| height | array | Yes | Array of height levels (10-300m) as strings |
| elevation | string | No | Elevation dataset (default: 'srtm30m') |
| landcover | string | No | Land cover dataset (default: 'esa2021') |
| sectors | string | No | Number of wind direction sectors (default: '12') |
Configuration Options
Elevation Datasets
- •
srtm30m- SRTM 30m resolution (default) - • Other elevation datasets available
Land Cover Datasets
- •
esa2021- ESA WorldCover 2021 (default) - • Other land cover datasets available
Wind Direction Sectors
- •
12sectors (default) - • Higher values for more detailed analysis
Example Request
wrg50_payload = {
"key": "your-api-key",
"name": "High-Resolution Wind Study",
"email": "user@example.com",
"longitude1": -95.5,
"latitude1": 36.0,
"longitude2": -95.3,
"latitude2": 36.2,
"height": ["80", "100", "120", "150"],
"elevation": "srtm30m",
"landcover": "esa2021",
"sectors": "16"
}
Response Format
{
"status": "ok",
"job_id": 12347
}
📍 Area Coverage
The WRG50/Sitewind® endpoint calculates the area coverage automatically based on your coordinate bounds. Minimum area 25x25km2 - Maximum area 150x150km2
Download Item Files API
Description
Download completed job files from WRF Time Series, WRG200, and WRG50/Sitewind® endpoints. Supports jobs with single or multiple output files.
Request Method
POST request to https://ul-renewables.com/api/download/
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| key | string | Yes | API write access key |
| string | Yes | User email address (must match job owner) | |
| job_id | integer | Yes | Job ID returned from job creation endpoints |
| file_index | integer | No | File index for multi-file jobs (default: 0) |
Usage Workflow
Create Job
Use WRF, WRG200, or WRG50/Sitewind® endpoint
Get Job ID
Save the returned job_id
Wait for Processing
Job processing time varies
Download Item Files
Use this endpoint to download
Example Request (Single File)
download_payload = {
"key": "your-api-key",
"email": "user@example.com",
"job_id": 12345
}
Example Request (Multi-File Job)
# First request without file_index to see available files
download_payload = {
"key": "your-api-key",
"email": "user@example.com",
"job_id": 12345
}
# If multiple files exist, specify file_index
download_payload_specific = {
"key": "your-api-key",
"email": "user@example.com",
"job_id": 12345,
"file_index": 1 # Download second file
}
Response Formats
Single File Response
Direct file download with appropriate headers:
Content-Type: application/zip
Content-Disposition: attachment; filename="file.zip"
Multiple Files Available
{
"status": "multiple_files",
"files": [
{"index": 0, "name": "wrg_100m.zip"},
{"index": 1, "name": "wrg_150m.zip"}
],
"message": "Multiple files available. Specify file_index parameter."
}
Complete Implementation Example
import requests
import json
def download_job_file(api_key, email, job_id, file_index=None):
"""Download job file with automatic multi-file handling"""
download_url = 'https://ul-renewables.com/api/downloadJobFile'
payload = {
"key": api_key,
"email": email,
"job_id": job_id
}
# Add file_index if specified
if file_index is not None:
payload["file_index"] = file_index
response = requests.post(download_url, json=payload)
if response.status_code == 200:
# Check if response is JSON (multiple files info) or file download
try:
json_response = response.json()
if json_response.get('status') == 'multiple_files':
print("Multiple files available:")
for file_info in json_response['files']:
print(f" Index {file_info['index']}: {file_info['name']}")
return json_response
except json.JSONDecodeError:
# Response is a file download
filename = f"job_{job_id}_file_{file_index or 0}.zip"
with open(filename, 'wb') as f:
f.write(response.content)
print(f"Downloaded: {filename}")
return filename
else:
print(f"Error: {response.status_code} - {response.text}")
return None
# Usage example
job_result = download_job_file("your-api-key", "user@example.com", 12345)
# If multiple files, download specific file
if isinstance(job_result, dict) and job_result.get('status') == 'multiple_files':
download_job_file("your-api-key", "user@example.com", 12345, file_index=0)
Common Error Scenarios
❌ Job Not Found
Check that job_id is correct and belongs to the specified user
⚠️ Job Still Processing
Wait for job completion before attempting download
📂 File Index Out of Range
Ensure file_index is within the available range for multi-file jobs
🔐 Access Denied
Verify that the email matches the job owner and API key is valid
Code Implementation
Main Function
import requests
import json
# API Configuration
API_KEY = 'your-api-key-here'
API_URL = 'https://ul-renewables.com/api'
# Common headers
headers = {
'Content-Type': 'application/json'
}
def make_api_request(payload, request_type):
"""Make API request and handle response"""
print(f"\n--- {request_type} REQUEST ---")
print(f"Payload: {json.dumps(payload, indent=2)}")
response = requests.post(API_URL, headers=headers, json=payload)
if response.status_code == 200:
print(f"✓ Success: {response.status_code}")
print(f"Response: {response.text[:1000]}")
return response.json() if response.text else None
else:
print(f"✗ Error: {response.status_code} - {response.text}")
return None
# 1. COMPASS STATS REQUEST
print("="*60)
print("TESTING COMPASS STATS DATA")
print("="*60)
compass_payload = {
"key": API_KEY,
"product": "stats",
"latitude": 36.12361,
"longitude": -95.37542,
"height": 250,
}
compass_result = make_api_request(compass_payload, "COMPASS STATS")
# 2. GLOBAL REANALYSIS REQUEST
print("\n" + "="*60)
print("TESTING GLOBAL REANALYSIS DATA")
print("="*60)
print("Available datasets: era5-100, era5-10, merra2-50")
reanalysis_payload = {
'key': API_KEY,
'latitude': 39.38832449859204,
'longitude': -97.63412475585938,
'dataset': 'era5-100',
'product': 'reanalysis',
}
reanalysis_result = make_api_request(reanalysis_payload, "GLOBAL REANALYSIS")
# 3. OPTIONAL: Test multiple datasets
print("\n" + "="*60)
print("TESTING MULTIPLE REANALYSIS DATASETS")
print("="*60)
datasets = ['era5-100', 'era5-10', 'merra2-50']
for dataset in datasets:
multi_payload = {
'key': API_KEY,
'latitude': 39.38832449859204,
'longitude': -97.63412475585938,
'dataset': dataset,
'product': 'reanalysis',
}
print(f"\nTesting dataset: {dataset}")
result = make_api_request(multi_payload, f"REANALYSIS - {dataset.upper()}")
print("\n" + "="*60)
print("API TESTING COMPLETE")
print("="*60)
Error Handling
HTTP Status Codes
Checks for successful responses (200)
Response Validation
Handles both JSON and file responses
Error Logging
Provides detailed error information
Usage Examples
Single COMPASS Request
compass_result = make_api_request(compass_payload, "COMPASS STATS")
if compass_result:
wind_speed = compass_result['windSpeed']
power_density = compass_result['meanPowerDensity']
Multiple Dataset Testing
datasets = ['era5-100', 'era5-10', 'merra2-50']
for dataset in datasets:
payload['dataset'] = dataset
result = make_api_request(payload, f"REANALYSIS - {dataset.upper()}")
Troubleshooting
Common Issues
❌ Invalid API Key
Ensure your API key is correct and active
⚠️ Coordinate Errors
Verify latitude/longitude are within valid ranges
📍 Dataset Availability
Check that the requested dataset covers your location
🌐 Network Timeouts
Implement retry logic for network issues
HTTP Response Codes
| Code | Status | Description | Action |
|---|---|---|---|
| 200 | Success | Request completed successfully | Process the response data |
| 400 | Bad Request | Invalid parameters in request | Check parameter format and values |
| 401 | Unauthorized | Invalid or missing API key | Verify API key is correct and active |
| 404 | Not Found | Invalid endpoint URL | Check the API endpoint URL |
| 500 | Server Error | Internal server error | Retry request or contact support |