API Reference

Astera delivers a single unified WorldState for any coordinate on Earth — weather, marine, astronomy, air quality, flood risk, and scored activity recommendations in one call.

Overview

All responses are JSON. All requests are GET. Coordinates are decimal degrees (WGS84). The API is stateless — no session or pagination.

The base URL is the same origin as this page when accessed via the dashboard. For direct API access, use the URL below.

Base URL

https://astera-api.onrender.com

All endpoints are prefixed with /api/v1/.

Authentication

Protected endpoints require an API key. Pass it in one of two ways:

MethodExample
X-API-Key headerX-API-Key: your_key_here
Bearer tokenAuthorization: Bearer your_key_here

Keys are provisioned by Harrow Point Software. Contact us to request access.

Rate limits

Requests are limited to 60 per minute per IP address. Exceeding this returns 429 Too Many Requests. Responses are cached for 5 minutes per location (1 km precision), so repeated calls for the same area are served instantly.

World State

Returns the full current WorldState for a coordinate: weather, marine, astronomy, air quality, flood risk, activity scores, and recommendations.

GET /api/v1/world-state

Parameters

ParameterTypeRequiredDescription
latfloatYesLatitude, −90 to 90
lonfloatYesLongitude, −180 to 180

Example request

curl "https://astera-api.onrender.com/api/v1/world-state?lat=51.5074&lon=-0.1278" \
  -H "X-API-Key: your_key_here"
const res = await fetch(
  'https://astera-api.onrender.com/api/v1/world-state?lat=51.5074&lon=-0.1278',
  { headers: { 'X-API-Key': 'your_key_here' } }
);
const data = await res.json();
import requests

r = requests.get(
    'https://astera-api.onrender.com/api/v1/world-state',
    params={'lat': 51.5074, 'lon': -0.1278},
    headers={'X-API-Key': 'your_key_here'}
)
data = r.json()

Response schema

{
  "location": {
    "name": "London",
    "country": "GB",
    "latitude": 51.5074,
    "longitude": -0.1278,
    "timezone": "Europe/London"
  },
  "time": {
    "requested_at": "2026-06-01T14:30:00Z",
    "local_time":   "2026-06-01T15:30:00+01:00",
    "is_daylight":  true
  },
  "weather": {
    "temperature_celsius":    18.4,
    "wind_speed_kph":         14.0,
    "wind_gust_kph":          22.0,
    "wind_direction_degrees": 240,
    "precipitation_mm":       0.0,
    "visibility_km":          24.1,
    "humidity_pct":           62.0,
    "uv_index":               4.2,
    "condition":              "partly_cloudy"
  },
  "marine": {
    "tide_state":      "rising",
    "next_low_tide":   "2026-06-01T19:42:00Z",
    "next_high_tide":  "2026-06-02T01:15:00Z",
    "wave_height_m":   0.4,
    "swell_period_sec": 8.2,
    "sea_temp_c":      14.1
  },
  "astronomy": {
    "sunrise":               "2026-06-01T04:51:00Z",
    "sunset":                "2026-06-01T21:08:00Z",
    "moon_phase":            "waxing_gibbous",
    "moon_illumination_pct": 72.3
  },
  "air_quality": {
    "aqi":        38,
    "category":   "Good",
    "pm25_ugm3":  4.2,
    "pm10_ugm3":  8.1,
    "no2_ugm3":   12.4,
    "o3_ugm3":    67.0
  },
  "risk": {
    "weather_warning": false,
    "flood_risk":      "low",
    "marine_risk":     "low",
    "air_quality_risk": "low"
  },
  "activity_scores": {
    "drone":        8,
    "stargazing":   3,
    "photography":  9,
    "coastal_walk": 7,
    "fishing":      6,
    "sailing":      7,
    "hiking":       9,
    "cycling":      8,
    "surfing":      3,
    "running":      9,
    "swimming":     4,
    "birdwatching": 7,
    "kayaking":     8
  },
  "recommendations": [
    {
      "type":    "photography",
      "score":   9,
      "summary": "Partly cloudy skies add drama and texture — good conditions for landscapes."
    },
    {
      "type":    "hiking",
      "score":   9,
      "summary": "Excellent hiking conditions. Enjoy the trail."
    }
  ]
}
Note: marine is null for inland locations. air_quality may be null if the provider is temporarily unavailable. All other fields are always present.

Forecast

Returns a daily 7-day forecast with aggregated weather and activity scores for each day.

GET /api/v1/forecast

Parameters

ParameterTypeRequiredDescription
latfloatYesLatitude, −90 to 90
lonfloatYesLongitude, −180 to 180
daysintNoNumber of days, 1–9. Defaults to 7.

Example request

curl "https://astera-api.onrender.com/api/v1/forecast?lat=51.5074&lon=-0.1278&days=7" \
  -H "X-API-Key: your_key_here"
const res = await fetch(
  'https://astera-api.onrender.com/api/v1/forecast?lat=51.5074&lon=-0.1278&days=7',
  { headers: { 'X-API-Key': 'your_key_here' } }
);
const { location, forecast } = await res.json();
import requests

r = requests.get(
    'https://astera-api.onrender.com/api/v1/forecast',
    params={'lat': 51.5074, 'lon': -0.1278, 'days': 7},
    headers={'X-API-Key': 'your_key_here'}
)
data = r.json()

Response schema

{
  "location": { ... },
  "forecast": [
    {
      "date": "2026-06-01",
      "weather": {
        "temperature_high_celsius": 21.3,
        "temperature_low_celsius":  12.8,
        "wind_speed_kph":           18.0,
        "wind_direction_degrees":   230,
        "precipitation_mm":         0.0,
        "visibility_km":            22.5,
        "condition":                "partly_cloudy"
      },
      "astronomy": {
        "sunrise":               "2026-06-01T04:51:00Z",
        "sunset":                "2026-06-01T21:08:00Z",
        "moon_phase":            "waxing_gibbous",
        "moon_illumination_pct": 72.3
      },
      "activity_scores": { ... },
      "best_activity": "hiking"
    }
  ]
}

Alerts

Register a webhook to be called automatically when an activity score meets your threshold. The poller checks all active rules every 15 minutes. A per-rule cooldown (default 60 minutes) prevents repeated firings.

Create an alert

POST /api/v1/alerts

Request body

FieldTypeRequiredDescription
latfloatYesLatitude of the location to watch
lonfloatYesLongitude of the location to watch
activitystringYesActivity key, e.g. drone
thresholdintYesMinimum score (1–10) to trigger the alert
webhook_urlstringYeshttps URL to POST to when the alert fires
labelstringNoHuman-friendly name for this rule
cooldown_minutesintNoMinimum gap between firings. Defaults to 60.
curl -X POST "https://astera-api.onrender.com/api/v1/alerts" \
  -H "X-API-Key: your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "lat": 51.5074,
    "lon": -0.1278,
    "activity": "drone",
    "threshold": 7,
    "webhook_url": "https://your-server.example.com/hooks/astera",
    "label": "London drone alert",
    "cooldown_minutes": 120
  }'
const res = await fetch('https://astera-api.onrender.com/api/v1/alerts', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your_key_here',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    lat: 51.5074, lon: -0.1278,
    activity: 'drone', threshold: 7,
    webhook_url: 'https://your-server.example.com/hooks/astera',
    label: 'London drone alert',
    cooldown_minutes: 120,
  }),
});
const alert = await res.json(); // contains the generated id

Webhook payload

When an alert fires, Astera sends a POST to your webhook_url with this body:

{
  "alert_id":  "a3f8c1d2b4e5f609",
  "label":     "London drone alert",
  "activity":  "drone",
  "score":     8,
  "threshold": 7,
  "fired_at":  "2026-06-01T07:15:00Z",
  "world_state": { ... }
}

List alerts

GET /api/v1/alerts

Returns all registered alert rules.

Delete an alert

DELETE /api/v1/alerts/{id}

Removes the alert rule with the given id. Returns 204 No Content on success.

Test an alert

POST /api/v1/alerts/{id}/test

Fetches the current world state for the rule's location and immediately fires the webhook — regardless of the current score or cooldown. Useful for verifying your endpoint is reachable before relying on the poller. Returns the activity, live score, and fired timestamp.

Health check

GET /api/v1/ping

Returns 200 OK with body pong. No authentication required. Useful for uptime monitoring and cold-start warm-up.

Activity scores

Every activity is scored 0–10 based on live conditions. Scores ≥ 5 appear in recommendations with a human-readable summary. All 13 scores are always present in the response, even for inland locations (marine-dependent activities score 0 when no coastal data is available).

🚁 Drone
drone
🔭 Stargazing
stargazing
📷 Photography
photography
🚶 Coastal Walk
coastal_walk
🎣 Fishing
fishing
⛵ Sailing
sailing
🥾 Hiking
hiking
🚴 Cycling
cycling
🏄 Surfing
surfing
🏃 Running
running
🏊 Swimming
swimming
🦅 Birdwatching
birdwatching
🛶 Kayaking
kayaking

Score interpretation

ScoreMeaning
8 – 10Excellent conditions
6 – 7Good conditions
4 – 5Fair — worth considering
1 – 3Poor — notable limiting factors
0Not viable (storm, no data, extreme conditions)

Weather conditions

The condition field uses a normalised string from the following set:

clear_sky · mainly_clear · partly_cloudy · overcast
fog · drizzle · rain · rain_showers · snow
thunderstorm · thunderstorm_with_hail · unknown

Risk levels

The risk object summarises hazard levels derived from live data.

FieldValuesSource
weather_warningtrue / falseWind > 60 km/h or thunderstorm
flood_risklow · moderate · high · severeEnvironment Agency (England & Wales); precipitation elsewhere
marine_risklow · moderate · highWave height thresholds
air_quality_risklow · moderate · high · unknownAQI (US EPA scale)

Data sources

DomainProviderCoverage
WeatherNorwegian Meteorological Institute (met.no)Global
Marine / wavesOpen-Meteo MarineGlobal coastal
TidesWorldTides → NOAA CO-OPS → Environment Agency (cascade)Global / US / England & Wales
AstronomyComputed (Meeus algorithms)Global
LocationNominatim / OpenStreetMapGlobal
Air qualityWAQIGlobal (major cities)
Flood riskEnvironment AgencyEngland & Wales