Wind Division API Documentation¶
Overview¶
The Tessellate Renewables Wind Division provides a comprehensive suite of APIs for wind energy analysis, forecasting, and site optimization. These endpoints support the full lifecycle of wind project development -- from initial site prospecting and terrain assessment through turbine selection, wake modeling, and energy yield forecasting.
Base URL: https://api.tessellate.energy/v1
Authentication: All endpoints require a valid API key passed via the Authorization
header using the Bearer scheme.
Rate Limits: Wind endpoints share the account-level rate limit (default 1,000 requests/minute for Pro plans). Long-running analysis jobs are metered separately and consume compute credits.
Endpoints¶
1. Quick Analysis¶
POST /wind/analysis/quick
Run a synchronous wind resource assessment for a single point location. Returns estimated annual energy production (AEP), wind rose, and Weibull distribution parameters. This endpoint is designed for rapid screening and returns results within seconds.
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
latitude |
float |
Yes | Site latitude (-90 to 90) |
longitude |
float |
Yes | Site longitude (-180 to 180) |
hub_height_m |
float |
No | Hub height in meters (default: 80) |
turbine_model |
string |
No | Turbine model ID from catalog (default: generic_3mw) |
data_source |
string |
No | Reanalysis dataset: era5, merra2, gfs (default: era5) |
years |
integer |
No | Number of historical years to analyze, 1-30 (default: 10) |
Response Schema:
{
"status": "completed",
"location": {
"latitude": 41.85,
"longitude": -87.65
},
"summary": {
"mean_wind_speed_ms": 7.2,
"mean_wind_direction_deg": 245.0,
"mean_power_density_wm2": 380.5,
"estimated_aep_mwh": 8520.3,
"capacity_factor_pct": 32.4,
"weibull_k": 2.1,
"weibull_a": 8.15,
"data_coverage_pct": 99.7
},
"wind_rose": {
"directions": [0, 22.5, 45, 67.5, 90, 112.5, 135, 157.5, 180, 202.5, 225, 247.5, 270, 292.5, 315, 337.5],
"frequencies_pct": [4.2, 3.1, 2.8, 3.5, 5.1, 6.2, 7.8, 9.1, 8.5, 7.2, 10.3, 11.4, 8.9, 5.6, 3.4, 2.9],
"mean_speeds_ms": [5.1, 4.8, 4.5, 5.2, 6.3, 6.8, 7.5, 8.2, 7.9, 7.1, 8.5, 9.1, 8.0, 6.2, 4.9, 4.3]
},
"metadata": {
"data_source": "era5",
"hub_height_m": 80,
"turbine_model": "generic_3mw",
"analysis_years": 10,
"analysis_period": "2014-01-01 to 2023-12-31",
"processing_time_ms": 1250
}
}
Status Codes:
| Code | Description |
|---|---|
200 |
Analysis completed successfully |
400 |
Invalid coordinates or parameters |
401 |
Missing or invalid API key |
402 |
Insufficient compute credits |
429 |
Rate limit exceeded |
500 |
Internal server error |
2. Full Async Analysis¶
Wind farm-level analysis runs asynchronously due to computational complexity. Submit a job and poll for results.
2a. Submit Analysis Job¶
POST /wind/analysis/run
Submit a full wind farm analysis including wake modeling, terrain effects, energy yield, and uncertainty quantification. Returns a job ID for tracking.
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Analysis name for reference |
boundary |
GeoJSON |
Yes | Site boundary as GeoJSON Polygon or MultiPolygon |
turbine_positions |
array |
No | Array of {lat, lon} for fixed layouts |
turbine_model |
string |
Yes | Turbine model ID from catalog |
hub_height_m |
float |
No | Override hub height (default: from turbine spec) |
wake_model |
string |
No | Wake model: jensen, bastankhah, gauss_curl (default: bastankhah) |
terrain_model |
string |
No | Terrain model: flat, speedup_rix, cfd_rans (default: speedup_rix) |
optimize_layout |
boolean |
No | Run layout optimization (default: false) |
optimization_algorithm |
string |
No | Algorithm: genetic, nsga2, gradient (default: genetic) |
max_turbines |
integer |
No | Maximum turbines for layout optimization |
min_spacing_diameters |
float |
No | Minimum spacing in rotor diameters (default: 3.0) |
data_source |
string |
No | Reanalysis dataset (default: era5) |
years |
integer |
No | Historical years to analyze (default: 20) |
uncertainty_analysis |
boolean |
No | Include Monte Carlo uncertainty (default: true) |
callback_url |
string |
No | Webhook URL for completion notification |
Response Schema:
{
"job_id": "wnd_job_7f3a2b1c",
"status": "queued",
"estimated_duration_s": 180,
"created_at": "2025-01-15T10:30:00Z",
"poll_url": "/v1/wind/analysis/wnd_job_7f3a2b1c"
}
2b. Get Analysis Status / Results¶
GET /wind/analysis/{job_id}
Poll for job status and retrieve results when complete.
Path Parameters:
| Field | Type | Description |
|---|---|---|
job_id |
string |
Job ID returned from the submission endpoint |
Query Parameters:
| Field | Type | Description |
|---|---|---|
include_timeseries |
boolean |
Include hourly time-series data (default: false) |
include_turbine_detail |
boolean |
Include per-turbine breakdown (default: true) |
Response Schema (completed):
{
"job_id": "wnd_job_7f3a2b1c",
"status": "completed",
"name": "Midwest Farm Phase 1",
"created_at": "2025-01-15T10:30:00Z",
"completed_at": "2025-01-15T10:33:12Z",
"processing_time_s": 192,
"farm_summary": {
"total_capacity_mw": 150.0,
"turbine_count": 50,
"gross_aep_mwh": 438500.0,
"net_aep_mwh": 394650.0,
"wake_loss_pct": 8.2,
"electrical_loss_pct": 2.0,
"availability_loss_pct": 3.0,
"net_capacity_factor_pct": 30.1,
"p50_aep_mwh": 394650.0,
"p75_aep_mwh": 378200.0,
"p90_aep_mwh": 361500.0,
"p99_aep_mwh": 340200.0,
"mean_wind_speed_ms": 7.8,
"turbulence_intensity_pct": 11.2
},
"turbine_results": [
{
"turbine_id": 1,
"latitude": 41.8510,
"longitude": -87.6520,
"gross_aep_mwh": 8770.0,
"net_aep_mwh": 8105.0,
"wake_loss_pct": 7.6,
"mean_wind_speed_ms": 7.9,
"mean_turbulence_intensity_pct": 10.8
}
],
"terrain_analysis": {
"max_elevation_m": 312.5,
"min_elevation_m": 285.0,
"mean_slope_deg": 2.3,
"max_rix_pct": 5.1,
"terrain_complexity": "simple"
},
"uncertainty": {
"method": "monte_carlo",
"iterations": 10000,
"wind_resource_uncertainty_pct": 4.5,
"wake_model_uncertainty_pct": 3.0,
"total_uncertainty_pct": 6.2
},
"metadata": {
"turbine_model": "vestas_v110_2mw",
"wake_model": "bastankhah",
"terrain_model": "speedup_rix",
"data_source": "era5",
"analysis_years": 20
}
}
Job Status Values:
| Status | Description |
|---|---|
queued |
Job is waiting in the processing queue |
processing |
Job is currently being computed |
completed |
Results are ready for retrieval |
failed |
Job failed; see error field for details |
cancelled |
Job was cancelled by the user |
3. Wind Forecast¶
POST /wind/forecast
Generate short-term or medium-term wind speed and power forecasts for a site. Uses ensemble NWP models with machine learning post-processing for improved accuracy.
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
latitude |
float |
Yes | Site latitude |
longitude |
float |
Yes | Site longitude |
hub_height_m |
float |
No | Hub height in meters (default: 80) |
turbine_model |
string |
No | Turbine model for power conversion |
horizon_hours |
integer |
No | Forecast horizon: 1-240 (default: 72) |
resolution_minutes |
integer |
No | Temporal resolution: 15, 30, or 60 (default: 60) |
include_ensemble |
boolean |
No | Include ensemble spread (default: false) |
nwp_models |
array |
No | NWP models to use: gfs, ecmwf, nam (default: ["gfs", "ecmwf"]) |
Response Schema:
{
"location": {
"latitude": 41.85,
"longitude": -87.65
},
"forecast_issued_at": "2025-01-15T12:00:00Z",
"horizon_hours": 72,
"resolution_minutes": 60,
"forecasts": [
{
"timestamp": "2025-01-15T13:00:00Z",
"wind_speed_ms": 8.2,
"wind_direction_deg": 230,
"temperature_c": -2.5,
"pressure_hpa": 1013.2,
"power_kw": 1850.0,
"confidence_low_ms": 6.8,
"confidence_high_ms": 9.6
}
],
"summary": {
"mean_wind_speed_ms": 7.5,
"max_wind_speed_ms": 14.2,
"min_wind_speed_ms": 2.1,
"mean_power_kw": 1620.0,
"total_energy_kwh": 116640.0,
"ramp_events": [
{
"start": "2025-01-16T06:00:00Z",
"end": "2025-01-16T10:00:00Z",
"type": "ramp_up",
"magnitude_ms": 8.5,
"confidence_pct": 78.0
}
]
},
"metadata": {
"nwp_models_used": ["gfs", "ecmwf"],
"ml_postprocessing": true,
"hub_height_m": 80,
"turbine_model": "generic_3mw"
}
}
4. Turbine Catalog¶
GET /wind/turbines
Retrieve the catalog of available turbine models with specifications and power curves.
Query Parameters:
| Field | Type | Description |
|---|---|---|
manufacturer |
string |
Filter by manufacturer name |
min_capacity_kw |
integer |
Minimum rated capacity in kW |
max_capacity_kw |
integer |
Maximum rated capacity in kW |
min_rotor_diameter_m |
float |
Minimum rotor diameter in meters |
search |
string |
Free-text search across model names |
page |
integer |
Page number (default: 1) |
page_size |
integer |
Results per page, max 100 (default: 20) |
Response Schema:
{
"turbines": [
{
"id": "vestas_v110_2mw",
"manufacturer": "Vestas",
"model": "V110-2.0",
"rated_power_kw": 2000,
"rotor_diameter_m": 110.0,
"hub_height_options_m": [80, 95, 110, 125],
"cut_in_speed_ms": 3.0,
"rated_speed_ms": 11.5,
"cut_out_speed_ms": 25.0,
"iec_class": "IIIA",
"swept_area_m2": 9503.0,
"specific_power_wm2": 210.5,
"power_curve": {
"wind_speeds_ms": [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25],
"power_kw": [0, 66, 152, 280, 480, 750, 1100, 1500, 1820, 1960, 1990, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000]
},
"ct_curve": {
"wind_speeds_ms": [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25],
"ct_values": [0.90, 0.88, 0.85, 0.82, 0.78, 0.72, 0.64, 0.52, 0.39, 0.30, 0.25, 0.21, 0.18, 0.16, 0.14, 0.12, 0.11, 0.10, 0.09, 0.08, 0.07, 0.06, 0.06]
}
}
],
"pagination": {
"page": 1,
"page_size": 20,
"total_items": 142,
"total_pages": 8
}
}
5. Terrain Analysis¶
POST /wind/terrain
Analyze terrain characteristics for a wind development site, including elevation profiles, slope gradients, roughness classification, and RIX (Ruggedness Index) computation.
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
boundary |
GeoJSON |
Yes | Site boundary as GeoJSON Polygon |
resolution_m |
float |
No | Grid resolution in meters (default: 30) |
include_roughness |
boolean |
No | Compute surface roughness map (default: true) |
include_elevation_profile |
boolean |
No | Include elevation cross-sections (default: true) |
profile_directions_deg |
array |
No | Directions for elevation profiles (default: [0, 90, 180, 270]) |
critical_slope_deg |
float |
No | Slope threshold for RIX calculation (default: 30.0) |
Response Schema:
{
"status": "completed",
"terrain_summary": {
"area_km2": 25.4,
"max_elevation_m": 520.3,
"min_elevation_m": 340.1,
"mean_elevation_m": 425.6,
"elevation_range_m": 180.2,
"mean_slope_deg": 4.8,
"max_slope_deg": 22.3,
"mean_rix_pct": 3.2,
"max_rix_pct": 12.1,
"terrain_complexity": "moderate",
"dominant_roughness_class": 2,
"dominant_roughness_length_m": 0.1
},
"roughness_map": {
"grid_resolution_m": 30,
"classes": [
{"class": 0, "description": "Water", "fraction_pct": 0.0},
{"class": 1, "description": "Open terrain", "fraction_pct": 45.2},
{"class": 2, "description": "Agricultural", "fraction_pct": 38.6},
{"class": 3, "description": "Forest/suburban", "fraction_pct": 16.2}
]
},
"elevation_profiles": [
{
"direction_deg": 0,
"label": "North-South",
"distances_m": [0, 100, 200, 300, 400, 500],
"elevations_m": [340.1, 355.2, 380.5, 410.8, 445.3, 470.1]
}
],
"constraints": {
"steep_slope_exclusion_pct": 5.2,
"buildable_area_km2": 24.1,
"access_difficulty": "low"
},
"metadata": {
"dem_source": "srtm_30m",
"land_cover_source": "corine_2018",
"processing_time_ms": 3200
}
}
6. Site Listing¶
GET /wind/sites
List all wind analysis sites associated with the authenticated user's organization.
Query Parameters:
| Field | Type | Description |
|---|---|---|
status |
string |
Filter by status: active, archived, prospecting |
search |
string |
Search by site name |
sort_by |
string |
Sort field: name, created_at, capacity_mw (default: created_at) |
sort_order |
string |
Sort direction: asc, desc (default: desc) |
page |
integer |
Page number (default: 1) |
page_size |
integer |
Results per page, max 100 (default: 20) |
Response Schema:
{
"sites": [
{
"id": "site_8a2f1c3d",
"name": "Prairie Wind Farm",
"status": "active",
"location": {
"latitude": 41.85,
"longitude": -87.65,
"region": "Illinois, US"
},
"capacity_mw": 150.0,
"turbine_count": 50,
"turbine_model": "vestas_v110_2mw",
"latest_aep_mwh": 394650.0,
"latest_capacity_factor_pct": 30.1,
"analysis_count": 12,
"created_at": "2024-06-15T08:00:00Z",
"updated_at": "2025-01-10T14:22:00Z"
}
],
"pagination": {
"page": 1,
"page_size": 20,
"total_items": 5,
"total_pages": 1
}
}
Code Examples¶
Python¶
import requests
API_KEY = "tr_live_abc123..."
BASE_URL = "https://api.tessellate.energy/v1"
HEADERS = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
# --- Quick Analysis ---
def quick_analysis(lat: float, lon: float, hub_height: float = 80) -> dict:
"""Run a quick wind resource assessment for a single point."""
response = requests.post(
f"{BASE_URL}/wind/analysis/quick",
headers=HEADERS,
json={
"latitude": lat,
"longitude": lon,
"hub_height_m": hub_height,
"turbine_model": "generic_3mw",
"data_source": "era5",
"years": 10,
},
)
response.raise_for_status()
return response.json()
result = quick_analysis(41.85, -87.65)
print(f"Mean wind speed: {result['summary']['mean_wind_speed_ms']} m/s")
print(f"Estimated AEP: {result['summary']['estimated_aep_mwh']} MWh")
# --- Full Async Analysis ---
import time
def submit_farm_analysis(name: str, boundary_geojson: dict, turbine_model: str) -> str:
"""Submit a full wind farm analysis and return the job ID."""
response = requests.post(
f"{BASE_URL}/wind/analysis/run",
headers=HEADERS,
json={
"name": name,
"boundary": boundary_geojson,
"turbine_model": turbine_model,
"wake_model": "bastankhah",
"optimize_layout": True,
"optimization_algorithm": "genetic",
"max_turbines": 50,
"uncertainty_analysis": True,
},
)
response.raise_for_status()
return response.json()["job_id"]
def poll_for_results(job_id: str, interval_s: int = 10, timeout_s: int = 600) -> dict:
"""Poll for job results until completion or timeout."""
start = time.time()
while time.time() - start < timeout_s:
response = requests.get(
f"{BASE_URL}/wind/analysis/{job_id}",
headers=HEADERS,
params={"include_turbine_detail": True},
)
response.raise_for_status()
data = response.json()
if data["status"] == "completed":
return data
if data["status"] == "failed":
raise RuntimeError(f"Analysis failed: {data.get('error')}")
print(f"Status: {data['status']} - waiting {interval_s}s...")
time.sleep(interval_s)
raise TimeoutError("Analysis did not complete within timeout")
boundary = {
"type": "Polygon",
"coordinates": [[
[-87.70, 41.80], [-87.60, 41.80],
[-87.60, 41.90], [-87.70, 41.90],
[-87.70, 41.80],
]],
}
job_id = submit_farm_analysis("Midwest Farm Phase 1", boundary, "vestas_v110_2mw")
results = poll_for_results(job_id)
print(f"Net AEP: {results['farm_summary']['net_aep_mwh']} MWh")
print(f"Wake losses: {results['farm_summary']['wake_loss_pct']}%")
# --- Wind Forecast ---
def get_forecast(lat: float, lon: float, horizon_hours: int = 72) -> dict:
"""Get a wind speed and power forecast for a location."""
response = requests.post(
f"{BASE_URL}/wind/forecast",
headers=HEADERS,
json={
"latitude": lat,
"longitude": lon,
"hub_height_m": 80,
"turbine_model": "generic_3mw",
"horizon_hours": horizon_hours,
"resolution_minutes": 60,
},
)
response.raise_for_status()
return response.json()
forecast = get_forecast(41.85, -87.65, horizon_hours=48)
for entry in forecast["forecasts"][:5]:
print(f"{entry['timestamp']}: {entry['wind_speed_ms']} m/s, {entry['power_kw']} kW")
# --- Turbine Catalog ---
def list_turbines(manufacturer: str = None, min_capacity_kw: int = None) -> dict:
"""List available turbine models with optional filters."""
params = {}
if manufacturer:
params["manufacturer"] = manufacturer
if min_capacity_kw:
params["min_capacity_kw"] = min_capacity_kw
response = requests.get(
f"{BASE_URL}/wind/turbines",
headers=HEADERS,
params=params,
)
response.raise_for_status()
return response.json()
turbines = list_turbines(manufacturer="Vestas", min_capacity_kw=2000)
for t in turbines["turbines"]:
print(f"{t['manufacturer']} {t['model']}: {t['rated_power_kw']} kW")
# --- Site Listing ---
def list_sites(status: str = "active") -> dict:
"""List all wind analysis sites for the organization."""
response = requests.get(
f"{BASE_URL}/wind/sites",
headers=HEADERS,
params={"status": status, "sort_by": "created_at", "sort_order": "desc"},
)
response.raise_for_status()
return response.json()
sites = list_sites()
for site in sites["sites"]:
print(f"{site['name']}: {site['capacity_mw']} MW, CF={site['latest_capacity_factor_pct']}%")
JavaScript¶
const API_KEY = "tr_live_abc123...";
const BASE_URL = "https://api.tessellate.energy/v1";
const headers = {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
};
// --- Quick Analysis ---
async function quickAnalysis(lat, lon, hubHeight = 80) {
const response = await fetch(`${BASE_URL}/wind/analysis/quick`, {
method: "POST",
headers,
body: JSON.stringify({
latitude: lat,
longitude: lon,
hub_height_m: hubHeight,
turbine_model: "generic_3mw",
data_source: "era5",
years: 10,
}),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
// --- Full Async Analysis with polling ---
async function submitFarmAnalysis(name, boundary, turbineModel) {
const response = await fetch(`${BASE_URL}/wind/analysis/run`, {
method: "POST",
headers,
body: JSON.stringify({
name,
boundary,
turbine_model: turbineModel,
wake_model: "bastankhah",
optimize_layout: true,
max_turbines: 50,
uncertainty_analysis: true,
}),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
return data.job_id;
}
async function pollForResults(jobId, intervalMs = 10000, timeoutMs = 600000) {
const start = Date.now();
while (Date.now() - start < timeoutMs) {
const response = await fetch(
`${BASE_URL}/wind/analysis/${jobId}?include_turbine_detail=true`,
{ headers }
);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
if (data.status === "completed") return data;
if (data.status === "failed") throw new Error(`Analysis failed: ${data.error}`);
console.log(`Status: ${data.status} - waiting...`);
await new Promise((resolve) => setTimeout(resolve, intervalMs));
}
throw new Error("Timeout waiting for analysis results");
}
// --- Wind Forecast ---
async function getForecast(lat, lon, horizonHours = 72) {
const response = await fetch(`${BASE_URL}/wind/forecast`, {
method: "POST",
headers,
body: JSON.stringify({
latitude: lat,
longitude: lon,
hub_height_m: 80,
horizon_hours: horizonHours,
resolution_minutes: 60,
}),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
// --- Turbine Catalog ---
async function listTurbines(params = {}) {
const query = new URLSearchParams(params).toString();
const response = await fetch(`${BASE_URL}/wind/turbines?${query}`, { headers });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
// --- Sites ---
async function listSites(status = "active") {
const response = await fetch(`${BASE_URL}/wind/sites?status=${status}`, { headers });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
// Usage
(async () => {
const result = await quickAnalysis(41.85, -87.65);
console.log(`Mean wind speed: ${result.summary.mean_wind_speed_ms} m/s`);
console.log(`Estimated AEP: ${result.summary.estimated_aep_mwh} MWh`);
const turbines = await listTurbines({ manufacturer: "Vestas", min_capacity_kw: 2000 });
turbines.turbines.forEach((t) => {
console.log(`${t.manufacturer} ${t.model}: ${t.rated_power_kw} kW`);
});
})();
curl¶
# --- Quick Analysis ---
curl -X POST "https://api.tessellate.energy/v1/wind/analysis/quick" \
-H "Authorization: Bearer tr_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"latitude": 41.85,
"longitude": -87.65,
"hub_height_m": 80,
"turbine_model": "generic_3mw",
"data_source": "era5",
"years": 10
}'
# --- Submit Full Analysis ---
curl -X POST "https://api.tessellate.energy/v1/wind/analysis/run" \
-H "Authorization: Bearer tr_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"name": "Midwest Farm Phase 1",
"boundary": {
"type": "Polygon",
"coordinates": [[
[-87.70, 41.80], [-87.60, 41.80],
[-87.60, 41.90], [-87.70, 41.90],
[-87.70, 41.80]
]]
},
"turbine_model": "vestas_v110_2mw",
"wake_model": "bastankhah",
"optimize_layout": true,
"uncertainty_analysis": true
}'
# --- Poll for Results ---
curl -X GET "https://api.tessellate.energy/v1/wind/analysis/wnd_job_7f3a2b1c?include_turbine_detail=true" \
-H "Authorization: Bearer tr_live_abc123..."
# --- Wind Forecast ---
curl -X POST "https://api.tessellate.energy/v1/wind/forecast" \
-H "Authorization: Bearer tr_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"latitude": 41.85,
"longitude": -87.65,
"hub_height_m": 80,
"turbine_model": "generic_3mw",
"horizon_hours": 72,
"resolution_minutes": 60
}'
# --- Turbine Catalog ---
curl -X GET "https://api.tessellate.energy/v1/wind/turbines?manufacturer=Vestas&min_capacity_kw=2000" \
-H "Authorization: Bearer tr_live_abc123..."
# --- Terrain Analysis ---
curl -X POST "https://api.tessellate.energy/v1/wind/terrain" \
-H "Authorization: Bearer tr_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"boundary": {
"type": "Polygon",
"coordinates": [[
[-87.70, 41.80], [-87.60, 41.80],
[-87.60, 41.90], [-87.70, 41.90],
[-87.70, 41.80]
]]
},
"resolution_m": 30,
"include_roughness": true,
"include_elevation_profile": true,
"critical_slope_deg": 30.0
}'
# --- List Sites ---
curl -X GET "https://api.tessellate.energy/v1/wind/sites?status=active&sort_by=created_at&sort_order=desc" \
-H "Authorization: Bearer tr_live_abc123..."
Error Handling¶
All endpoints return errors in a consistent format:
{
"error": {
"code": "INVALID_COORDINATES",
"message": "Latitude must be between -90 and 90 degrees.",
"details": {
"field": "latitude",
"value": 95.0,
"constraint": "range(-90, 90)"
}
}
}
Common Error Codes:
| Code | HTTP Status | Description |
|---|---|---|
INVALID_COORDINATES |
400 | Latitude or longitude out of range |
INVALID_BOUNDARY |
400 | GeoJSON boundary is malformed or self-intersecting |
TURBINE_NOT_FOUND |
404 | Specified turbine model does not exist in catalog |
JOB_NOT_FOUND |
404 | Analysis job ID does not exist |
AUTH_REQUIRED |
401 | Missing or invalid API key |
INSUFFICIENT_CREDITS |
402 | Account does not have enough compute credits |
RATE_LIMITED |
429 | Too many requests; retry after Retry-After header value |
BOUNDARY_TOO_LARGE |
400 | Site boundary exceeds maximum area (10,000 km^2) |
DATA_UNAVAILABLE |
503 | Requested reanalysis data is temporarily unavailable |
Webhooks¶
For async analysis jobs, you can provide a callback_url to receive a notification when
the job completes. The webhook payload is:
{
"event": "wind.analysis.completed",
"job_id": "wnd_job_7f3a2b1c",
"status": "completed",
"timestamp": "2025-01-15T10:33:12Z",
"result_url": "https://api.tessellate.energy/v1/wind/analysis/wnd_job_7f3a2b1c"
}
The webhook is sent as an HTTP POST with a X-Tessellate-Signature header for
verification. See the Webhooks documentation for signature validation
details.